1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
26 #include <sfx2/docfile.hxx>
27 #include <sfx2/signaturestate.hxx>
29 #include <com/sun/star/task/InteractionHandler.hpp>
30 #include <com/sun/star/task/XStatusIndicator.hpp>
31 #include <com/sun/star/uno/Reference.h>
32 #include <com/sun/star/ucb/XContent.hpp>
33 #include <com/sun/star/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>
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>
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
;
148 struct ReadOnlyMediumEntry
150 ReadOnlyMediumEntry(std::shared_ptr
<std::recursive_mutex
> pMutex
,
151 std::shared_ptr
<bool> pIsDestructed
)
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
;
169 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
171 bool IsSystemFileLockingUsed()
173 #if HAVE_FEATURE_MACOSX_SANDBOX
176 return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
181 bool IsOOoLockFileUsed()
183 #if HAVE_FEATURE_MACOSX_SANDBOX
186 return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get();
192 return officecfg::Office::Common::Misc::UseLocking::get();
197 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
198 bool IsWebDAVLockingUsed()
200 return officecfg::Office::Common::Misc::UseWebDAVFileLocking::get();
204 /// Gets default attributes of a file:// URL.
205 sal_uInt64
GetDefaultFileAttributes(const OUString
& rURL
)
209 if (!comphelper::isFileUrl(rURL
))
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
)
220 osl::DirectoryItem aItem
;
221 if (osl::DirectoryItem::get(rURL
, aItem
) != osl::DirectoryItem::E_None
)
224 osl::FileStatus
aStatus(osl_FileStatus_Mask_Attributes
);
225 if (aItem
.getFileStatus(aStatus
) != osl::DirectoryItem::E_None
)
228 nRet
= aStatus
.getAttributes();
232 /// Determines if rURL is safe to move or not.
233 bool IsFileMovable(const INetURLObject
& rURL
)
237 // Hide extension macOS-specific file property would be lost.
241 if (rURL
.GetProtocol() != INetProtocol::File
)
242 // Not a file:// URL.
246 OUString sPath
= rURL
.getFSysPath(FSysStyle::Unix
);
251 if (lstat(sPath
.toUtf8().getStr(), &buf
) != 0)
254 // Hardlink or symlink: osl::File::move() doesn't play with these nicely.
255 if (buf
.st_nlink
> 1 || S_ISLNK(buf
.st_mode
))
258 if (tools::IsMappedWebDAVPath(rURL
.GetMainURL(INetURLObject::DecodeMechanism::NONE
)))
266 class CheckReadOnlyTaskTerminateListener
267 : public ::cppu::WeakImplHelper
<css::frame::XTerminateListener
>
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
;
282 class CheckReadOnlyTask
: public comphelper::ThreadTask
285 CheckReadOnlyTask(const std::shared_ptr
<comphelper::ThreadTaskTag
>& pTag
);
286 ~CheckReadOnlyTask();
288 virtual void doWork() override
;
291 rtl::Reference
<CheckReadOnlyTaskTerminateListener
> m_xListener
;
294 } // anonymous namespace
296 CheckReadOnlyTask::CheckReadOnlyTask(const std::shared_ptr
<comphelper::ThreadTaskTag
>& 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
)
318 xDesktop
->removeTerminateListener(m_xListener
);
326 CheckReadOnlyTaskTerminateListener::disposing(const css::lang::EventObject
& /*Source*/)
331 CheckReadOnlyTaskTerminateListener::queryTermination(const css::lang::EventObject
& /*aEvent*/)
336 CheckReadOnlyTaskTerminateListener::notifyTermination(const css::lang::EventObject
& /*aEvent*/)
338 std::unique_lock
<std::mutex
> lock(mMutex
);
339 bIsTerminated
= true;
348 StreamMode m_nStorOpenMode
;
351 ::ucbhelper::Content aContent
;
352 bool bUpdatePickList
:1;
354 bool bDownloadDone
: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;
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;
371 bool m_bInputStreamIsReadOnly
:1;
373 bool m_bDisableFileSync
= false;
374 bool m_bNotifyWhenEditable
= false;
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
;
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();
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),
441 bDownloadDone( true ),
443 bUseInteractionHandler( true ),
444 bAllowDefaultIntHdl( false ),
445 bDisposeStorage( false ),
446 bStorageBasedOnInStream( false ),
447 m_bSalvageMode( false ),
448 m_bVersionsAlreadyLoaded( 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),
458 m_bInputStreamIsReadOnly(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();
474 std::unique_lock
<std::recursive_mutex
> chkEditLock
;
475 if (m_pCheckEditableWorkerMutex
!= nullptr)
476 chkEditLock
= std::unique_lock
<std::recursive_mutex
>(*m_pCheckEditableWorkerMutex
);
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();
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
)
520 uno::Reference
< task::XInteractionHandler
> xHandler
= GetInteractionHandler();
522 if ( !xHandler
.is() )
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);
593 pItem
->GetValue() >>= xContent
;
599 pImpl
->aContent
= ::ucbhelper::Content( xContent
, pCommandEnv
, comphelper::getProcessComponentContext() );
601 catch ( const Exception
& )
607 // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's used.");
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
)
624 const SfxStringItem
* pBaseURLItem
= GetItemSet()->GetItem
<SfxStringItem
>(SID_DOC_BASEURL
);
626 aBaseURL
= pBaseURLItem
->GetValue();
627 else if (!utl::ConfigManager::IsFuzzing() && GetContent().is())
631 Any aAny
= pImpl
->aContent
.getPropertyValue("BaseURI");
634 catch ( const css::uno::Exception
& )
638 if ( aBaseURL
.isEmpty() )
639 aBaseURL
= GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE
);
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()) )
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();
677 return pImpl
->m_pInStream
.get();
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
)
705 if ( pImpl
->m_pInStream
&& !GetContent().is() && !bInDestruction
)
711 pImpl
->m_pInStream
.reset();
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();
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
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);
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
) );
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() )
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();
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()
810 if( pImpl
->m_pInStream
)
812 SvLockBytes
* pBytes
= pImpl
->m_pInStream
->GetLockBytes();
814 pBytes
->SetSynchronMode();
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 )
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
;
853 bool SfxMedium::IsStorage()
855 if ( pImpl
->xStorage
.is() )
858 if ( pImpl
->m_bTriedStorage
)
859 return pImpl
->bIsStorage
;
861 if ( pImpl
->pTempFile
)
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);
889 bPreview
= pPreview
->GetValue();
892 const SfxStringItem
* pFlags
= SfxItemSet::GetItem
<SfxStringItem
>(GetItemSet(), SID_OPTIONS
, false);
895 OUString aFileFlags
= pFlags
->GetValue();
896 aFileFlags
= aFileFlags
.toAsciiUpperCase();
897 if ( -1 != aFileFlags
.indexOf( 'B' ) )
906 void SfxMedium::StorageBackup_Impl()
908 ::ucbhelper::Content aOriginalContent
;
909 Reference
< css::ucb::XCommandEnvironment
> xDummyEnv
;
911 bool bBasedOnOriginalFile
=
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()
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() )
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();
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
)
968 uno::Sequence
< beans::NamedValue
> aEncryptionData
;
969 if ( !GetEncryptionData_Impl( pImpl
->m_pSet
.get(), aEncryptionData
) )
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.
1008 OUString
tryMSOwnerFiles(const OUString
& sDocURL
)
1010 svt::MSODocumentLockFile
aMSOLockFile(sDocURL
);
1011 LockFileEntry aData
;
1014 aData
= aMSOLockFile
.GetLockData();
1016 catch( const uno::Exception
& )
1021 OUString sUserData
= aData
[LockFileComponent::OOOUSERNAME
];
1023 if (!sUserData
.isEmpty())
1024 sUserData
+= " (MS Office)"; // Mention the used office suite
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
)
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
);
1055 ::rtl::Reference
< ::ucbhelper::InteractionRequest
> xInteractionRequestImpl
;
1057 sal_Int32 nContinuations
= 3;
1061 aInfo
= aData
[LockFileComponent::EDITTIME
];
1063 xInteractionRequestImpl
= new ::ucbhelper::InteractionRequest( uno::makeAny(
1064 document::OwnLockOnDocumentRequest( OUString(), uno::Reference
< uno::XInterface
>(), aDocumentURL
, aInfo
, !bIsLoading
) ) );
1068 // Use a fourth continuation in case there's no filesystem lock:
1069 // "Ignore lock file and open/replace the document"
1070 if (!bHandleSysLocked
)
1073 if ( !aData
[LockFileComponent::OOOUSERNAME
].isEmpty() )
1074 aInfo
= aData
[LockFileComponent::OOOUSERNAME
];
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 ) );
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;
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
1158 GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY
, true));
1160 nResult
= ShowLockResult::Try
;
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 ) );
1173 SetError(ERRCODE_IO_ACCESSDENIED
);
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();
1187 ::rtl::Reference
< ::ucbhelper::InteractionRequest
> xIgnoreRequestImpl
;
1191 case MessageDlg::LockFileIgnore
:
1192 xIgnoreRequestImpl
= new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileIgnoreRequest() ));
1194 case MessageDlg::LockFileCorrupt
:
1195 xIgnoreRequestImpl
= new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileCorruptRequest() ));
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
);
1215 else if (!uno::Reference
<task::XInteractionApprove
>(xSelected
.get(), uno::UNO_QUERY
).is())
1217 // user selected "Notify"
1218 pImpl
->m_bNotifyWhenEditable
= true;
1219 AddToCheckEditableWorkerList();
1223 GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY
, true));
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
) {
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
1257 (void) bTryIgnoreLockFile
;
1259 return LockFileResult::Succeeded
;
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...
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
;
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();
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];
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
;
1341 bUIStatus
= ShowLockedDocumentDialog(aLockData
, bLoading
, false,
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
);
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 );
1389 eResult
= LockFileResult::Succeeded
;
1391 catch ( const uno::Exception
& )
1393 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
1398 if (!IsLockingUsed())
1399 return LockFileResult::Succeeded
;
1400 if (GetURLObject().HasError())
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
;
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
1457 // let the stream be opened to check the system file locking
1459 if (GetError() != ERRCODE_NONE
) {
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();
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.
1522 else if (bLoading
&& !bNoUI
)
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
)
1535 // take the ownership over the lock file
1536 aLockFile
.OverwriteOwnLockFile();
1539 pMSOLockFile
->OverwriteOwnLockFile();
1545 LockFileEntry aData
;
1548 aData
= aLockFile
.GetLockData();
1550 catch (const io::WrongFormatException
&)
1552 // we get empty or corrupt data
1554 if (!bIoErr
&& bLoading
&& !bNoUI
)
1555 bResult
= ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt
);
1557 // not show the Lock Document Dialog
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
];
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
1582 if ( !bResult
&& !bIoErr
)
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();
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
;
1617 // this is no file URL, check whether the file is readonly
1618 bResult
= !bContentReadonly
;
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
);
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 );
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" );
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
1668 if ( bCreateTempFile
)
1669 CreateTempFile( false );
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 );
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
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;
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
),
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();
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
1771 short nVersion
= pVersion
->GetValue();
1773 nVersion
= static_cast<short>(pImpl
->aVersions
.getLength()) + nVersion
;
1774 else // nVersion > 0; pVersion->GetValue() != 0 was the condition to this block
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
);
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;
1804 osl::FileBase::getSystemPathFromFileURL( aTmpName
, aTemp
);
1805 SetPhysicalName_Impl( aTemp
);
1807 pImpl
->bIsTemp
= true;
1808 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY
, true ) );
1810 pImpl
->aVersions
.realloc(0);
1813 bResetStorage
= true;
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() )
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
1860 return pImpl
->m_xZipStorage
;
1864 void SfxMedium::CloseZipStorage_Impl()
1866 if ( pImpl
->m_xZipStorage
.is() )
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
)
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
,
1919 if ( pImpl
->m_nStorOpenMode
!= nStorOpen
)
1921 pImpl
->m_nStorOpenMode
= nStorOpen
;
1925 if ( pImpl
->xStorage
.is() )
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 );
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
;
1958 bool SfxMedium::StorageCommit_Impl()
1960 bool bResult
= false;
1961 Reference
< css::ucb::XCommandEnvironment
> xDummyEnv
;
1962 ::ucbhelper::Content aOriginalContent
;
1964 if ( pImpl
->xStorage
.is() )
1968 uno::Reference
< embed::XTransactedObject
> xTrans( pImpl
->xStorage
, uno::UNO_QUERY
);
1974 CloseZipStorage_Impl();
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!" );
2002 SetError(ERRCODE_IO_GENERAL
);
2004 catch ( const uno::Exception
& )
2006 //TODO/LATER: improve error handling
2007 SetError(ERRCODE_IO_GENERAL
);
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
;
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() )
2055 if ( pImpl
->xStorage
.is() )
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
2073 && ::utl::UCBContentHelper::IsDocument(
2074 aDest
.GetMainURL(INetURLObject::DecodeMechanism::NONE
))))
2076 Reference
< XInputStream
> aTempInput
= aTempCont
.openStream();
2077 aOriginalContent
.writeStream( aTempInput
, bOverWrite
);
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
)
2088 // Adjust attributes, source might be created with
2089 // the osl_File_OpenFlag_Private flag.
2090 osl::File::setAttributes(aDestMainURL
, nAttributes
);
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
);
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
;
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
;
2144 if ( pImpl
->pTempFile
)
2146 pImpl
->pTempFile
->EnableKillingFile();
2147 pImpl
->pTempFile
.reset();
2150 else if ( bTransactStarted
)
2152 UseBackupToRestore_Impl( aOriginalContent
, xDummyEnv
);
2156 pImpl
->m_eError
= ERRCODE_IO_CANTREAD
;
2160 bool SfxMedium::TryDirectTransfer( const OUString
& aURL
, SfxItemSet
const & aTargetSet
)
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();
2183 if ( xInStream
.is() )
2187 uno::Reference
< io::XSeekable
> xSeek( xInStream
, uno::UNO_QUERY
);
2191 nPos
= xSeek
->getPosition();
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;
2204 aInsertArg
.ReplaceExisting
= true; // default is overwrite existing files
2207 aCmdArg
<<= aInsertArg
;
2208 aTargetContent
.executeCommand( "insert",
2212 xSeek
->seek( nPos
);
2216 catch( const uno::Exception
& )
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
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() ) )
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() )
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
) )
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
);
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
& )
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
);
2310 if ( !pImpl
->aContent
.get().is() )
2312 pImpl
->m_eError
= ERRCODE_IO_NOTEXISTS
;
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
)
2342 SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL
<< "'" );
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
;
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()
2376 aDestURL
= sParentUrl
;
2379 // LongName wasn't defined anywhere, only used here... get the Title instead
2380 // as it's less probably empty
2382 Any aAny
= aDestContent
.getPropertyValue("Title");
2384 aAny
= aDestContent
.getPropertyValue( "ObjectId" );
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
;
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() )
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
;
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;
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);
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
;
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
,
2540 NameClash::OVERWRITE
,
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
,
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() )
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
) ) )
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
,
2627 NameClash::OVERWRITE
,
2629 pImpl
->m_aBackupURL
= aDest
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
2630 pImpl
->m_bRemoveBackup
= false;
2633 catch ( const css::uno::Exception
& )
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();
2663 SAL_WARN( "sfx.doc", "Couldn't remove backup file!");
2668 pImpl
->m_aBackupURL
.clear();
2672 void SfxMedium::GetLockingStream_Impl()
2674 if ( GetURLObject().GetProtocol() != INetProtocol::File
2675 || pImpl
->m_xLockingStream
.is() )
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() )
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() ) )
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
;
2739 uno::Sequence
< beans::PropertyValue
> xProps
;
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!");
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 ) );
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
;
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();
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
;
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
);
2879 if ( pImpl
->m_pInStream
&& !aLink
.IsSet() )
2881 while( !pImpl
->bDownloadDone
&& !Application::IsQuit())
2882 Application::Yield();
2887 void SfxMedium::Init_Impl()
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" );
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
);
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)
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);
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
2973 SID_FILE_NAME
, INetURLObject( pImpl
->m_aLogicName
).GetMainURL( INetURLObject::DecodeMechanism::NONE
) ) );
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
)
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() )
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() )
3055 nHash
= ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd
);
3059 rtl_TextEncoding nEncoding
= osl_getThreadTextEncoding();
3060 nHash
= ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd
, nEncoding
);
3068 void SfxMedium::Close(bool bInDestruction
)
3070 if ( pImpl
->xStorage
.is() )
3075 CloseStreams_Impl(bInDestruction
);
3077 UnlockFile( false );
3080 void SfxMedium::CloseAndRelease()
3082 if ( pImpl
->xStorage
.is() )
3087 CloseAndReleaseStreams_Impl();
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
;
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())
3116 if ( pImpl
->m_bLocked
)
3118 // an interaction handler should be used for authentication, if needed
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" );
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
)
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
)
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;
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
)
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;
3266 pImpl
->m_bRemote
= GetName().startsWith("private:msgid");
3270 // As files that are written to the remote transmission must also be able
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
;
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();
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;
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();
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();
3352 pTmpFile
->EnableKillingFile();
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
;
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
;
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
);
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
);
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);
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
);
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
;
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!" );
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
) );
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!" );
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
) );
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
3503 Close(/*bInDestruction*/true);
3505 if( !pImpl
->bIsTemp
|| pImpl
->m_aName
.isEmpty() )
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
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() )
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() )
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());
3667 for ( n
=0; n
<aLongs
.size(); ++n
)
3668 if ( nVer
<aLongs
[n
] )
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 )
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() )
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
;
3710 void SfxMedium::SaveVersionList_Impl()
3712 if ( !GetStorage().is() )
3715 if ( !pImpl
->aVersions
.hasElements() )
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
3736 bReadOnly
= !( GetOpenMode() & StreamMode::WRITE
);
3738 // c) the API can force the readonly state!
3741 const SfxBoolItem
* pItem
= SfxItemSet::GetItem
<SfxBoolItem
>(GetItemSet(), SID_DOC_READONLY
, false);
3743 bReadOnly
= pItem
->GetValue();
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,
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
);
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.
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
3823 void SfxMedium::CreateTempFile( bool bReplace
)
3825 if ( pImpl
->pTempFile
)
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
);
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
,
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
)
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
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;
3904 CloseOutStream_Impl();
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;
3914 if ( !bTransferSuccess
)
3916 SetError(ERRCODE_IO_CANTWRITE
);
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
);
3940 CloseOutStream_Impl();
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!");
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());
3968 uno::Reference
< embed::XStorage
> xWriteableZipStor
;
3970 // we can reuse the temporary file if there is one already
3971 CreateTempFile( false );
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
&)
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(
4000 embed::ElementModes::READWRITE
);
4001 if ( !xMetaInf
.is() )
4002 throw uno::RuntimeException();
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
);
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
4028 else if (xWriteableZipStor
.is())
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
);
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
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
))
4058 catch ( const uno::Exception
& )
4060 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
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!");
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
) );
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 );
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
&)
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(
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
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;
4170 bSuccess
= xSigner
->signSignatureLine(
4171 GetZipStorageToSign_Impl(), xStream
, aSignatureLineId
, xCert
,
4172 xValidGraphic
, xInvalidGraphic
, aComment
);
4174 bSuccess
= xSigner
->signDocumentContent(GetZipStorageToSign_Impl(),
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
4189 else if (xWriteableZipStor
.is())
4192 uno::Reference
<io::XStream
> xStream
;
4194 bool bSuccess
= false;
4197 bSuccess
= xSigner
->signSignatureLine(
4198 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream
, aSignatureLineId
,
4199 xCert
, xValidGraphic
, xInvalidGraphic
, aComment
);
4203 // We need read-write to be able to add the signature relation.
4204 bSuccess
=xSigner
->signDocumentContent(
4205 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream
);
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
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
))
4228 catch ( const uno::Exception
& )
4230 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
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
)
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
,
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
,
4294 NameClash::OVERWRITE
);
4295 aResult
= aNewTempFileURL
;
4297 catch( const uno::Exception
& )
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();
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
& )
4337 OUString
SfxMedium::SwitchDocumentToTempFile()
4339 // the method returns empty string in case of failure
4341 OUString aOrigURL
= pImpl
->m_aLogicName
;
4343 if ( !aOrigURL
.isEmpty() )
4345 sal_Int32 nPrefixLen
= aOrigURL
.lastIndexOf( '.' );
4346 OUString
const aExt
= (nPrefixLen
== -1)
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 );
4363 SetPhysicalName_Impl( OUString() );
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
);
4375 LockOrigFileOnDemand( false, false );
4379 if ( pImpl
->xStream
.is() )
4383 xOptStorage
->writeAndAttachToStream( pImpl
->xStream
);
4384 pImpl
->xStorage
= xStorage
;
4387 catch( const uno::Exception
& )
4391 if ( aResult
.isEmpty() )
4394 SetPhysicalName_Impl( OUString() );
4395 SetName( aOrigURL
);
4398 // set the readonly state back
4399 pImpl
->m_nStorOpenMode
= SFX_STREAM_READONLY
;
4400 GetItemSet()->Put( SfxBoolItem(SID_DOC_READONLY
, true));
4403 pImpl
->xStorage
= xStorage
;
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 );
4426 SetPhysicalName_Impl( OUString() );
4429 // open the temporary file based document
4431 LockOrigFileOnDemand( false, false );
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
;
4446 catch( const uno::Exception
& )
4453 SetPhysicalName_Impl( OUString() );
4454 SetName( aOrigURL
);
4456 pImpl
->xStorage
= xStorage
;
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
)
4497 CancelCheckEditableEntry();
4499 if (pImpl
->m_pCheckEditableWorkerMutex
== nullptr)
4501 pImpl
->m_pCheckEditableWorkerMutex
= std::make_shared
<std::recursive_mutex
>();
4502 if (pImpl
->m_pCheckEditableWorkerMutex
== nullptr)
4506 pImpl
->m_pIsDestructed
= std::make_shared
<bool>(false);
4507 if (pImpl
->m_pIsDestructed
== nullptr)
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
));
4527 bAddNewEntry
= true;
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)
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)
4576 pMed
->CancelCheckEditableEntry(false);
4578 uno::Reference
<task::XInteractionHandler
> xHandler
= pMed
->GetInteractionHandler();
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
);
4615 bool SfxMedium::CheckCanGetLockfile() const
4617 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
4618 bool bCanReload
= true;
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
4636 catch (const uno::Exception
&)
4638 // locked from other app
4641 LockFileEntry aOwnData
= svt::LockFileCommon::GenerateOwnEntry();
4643 = aOwnData
[LockFileComponent::SYSUSERNAME
] == aData
[LockFileComponent::SYSUSERNAME
];
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
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
&)
4675 catch (const uno::Exception
&)
4683 // worker thread method, should only be one thread globally
4684 void CheckReadOnlyTask::doWork()
4686 if (m_xListener
== nullptr)
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
4697 // must have timed-out
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;
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)
4719 std::unique_lock
<std::recursive_mutex
> medLock(*(roEntry
->_pMutex
));
4720 if (*(roEntry
->_pIsDestructed
) || pMed
->GetWorkerReloadEvent() != nullptr)
4724 pMed
->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::WithCharset
));
4725 if (aFile
.open(osl_File_OpenFlag_Write
) != osl::FileBase::E_None
)
4728 if (!pMed
->CheckCanGetLockfile())
4731 if (aFile
.close() != osl::FileBase::E_None
)
4734 // we can load, ask user
4735 ImplSVEvent
* pEvent
= Application::PostUserEvent(
4736 LINK(nullptr, SfxMedium
, ShowReloadEditableDialog
), pMed
);
4737 pMed
->SetWorkerReloadEvent(pEvent
);
4741 for (auto it
= g_existingReadOnlyDocs
.begin(); it
!= g_existingReadOnlyDocs
.end(); )
4743 if (checkForErase(it
->first
, it
->second
))
4744 it
= g_existingReadOnlyDocs
.erase(it
);
4751 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */