sfx2: avoid showing 2 dialogs in case WebDAV LOCK request fails
[LibreOffice.git] / sfx2 / source / doc / docfile.cxx
blob8c0d101c4ea91ffeed0c7199324d842814e92b41
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #ifdef UNX
23 #include <sys/stat.h>
24 #endif
26 #include <sfx2/docfile.hxx>
27 #include <sfx2/signaturestate.hxx>
29 #include <uno/mapping.hxx>
30 #include <com/sun/star/task/InteractionHandler.hpp>
31 #include <com/sun/star/task/XStatusIndicator.hpp>
32 #include <com/sun/star/uno/Reference.h>
33 #include <com/sun/star/ucb/XContent.hpp>
34 #include <com/sun/star/container/XChild.hpp>
35 #include <com/sun/star/document/XDocumentRevisionListPersistence.hpp>
36 #include <com/sun/star/document/LockedDocumentRequest.hpp>
37 #include <com/sun/star/document/LockedOnSavingRequest.hpp>
38 #include <com/sun/star/document/OwnLockOnDocumentRequest.hpp>
39 #include <com/sun/star/document/LockFileIgnoreRequest.hpp>
40 #include <com/sun/star/document/LockFileCorruptRequest.hpp>
41 #include <com/sun/star/document/ChangedByOthersRequest.hpp>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 #include <com/sun/star/embed/XTransactedObject.hpp>
44 #include <com/sun/star/embed/ElementModes.hpp>
45 #include <com/sun/star/embed/UseBackupException.hpp>
46 #include <com/sun/star/embed/XOptimizedStorage.hpp>
47 #include <com/sun/star/graphic/XGraphic.hpp>
48 #include <com/sun/star/ucb/ContentCreationException.hpp>
49 #include <com/sun/star/ucb/InteractiveIOException.hpp>
50 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
51 #include <com/sun/star/ucb/CommandFailedException.hpp>
52 #include <com/sun/star/ucb/CommandAbortedException.hpp>
53 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
54 #include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
55 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
56 #include <com/sun/star/ucb/Lock.hpp>
57 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
58 #include <com/sun/star/ucb/XContentIdentifierFactory.hpp>
59 #include <com/sun/star/ucb/XContentProvider.hpp>
60 #include <com/sun/star/ucb/XProgressHandler.hpp>
61 #include <com/sun/star/ucb/XCommandInfo.hpp>
62 #include <com/sun/star/io/XOutputStream.hpp>
63 #include <com/sun/star/io/XInputStream.hpp>
64 #include <com/sun/star/io/XTruncate.hpp>
65 #include <com/sun/star/io/XStreamListener.hpp>
66 #include <com/sun/star/io/XSeekable.hpp>
67 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
68 #include <com/sun/star/lang/XInitialization.hpp>
69 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
70 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
71 #include <com/sun/star/ucb/NameClash.hpp>
72 #include <com/sun/star/ucb/TransferInfo.hpp>
73 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
74 #include <com/sun/star/ucb/OpenMode.hpp>
75 #include <com/sun/star/beans/NamedValue.hpp>
76 #include <com/sun/star/beans/PropertyValue.hpp>
77 #include <com/sun/star/security/DocumentSignatureInformation.hpp>
78 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
79 #include <com/sun/star/security/XCertificate.hpp>
80 #include <tools/urlobj.hxx>
81 #include <tools/fileutil.hxx>
82 #include <unotools/configmgr.hxx>
83 #include <unotools/tempfile.hxx>
84 #include <comphelper/fileurl.hxx>
85 #include <comphelper/processfactory.hxx>
86 #include <comphelper/interaction.hxx>
87 #include <comphelper/sequence.hxx>
88 #include <comphelper/simplefileaccessinteraction.hxx>
89 #include <framework/interaction.hxx>
90 #include <unotools/streamhelper.hxx>
91 #include <unotools/localedatawrapper.hxx>
92 #include <utility>
93 #include <svl/stritem.hxx>
94 #include <svl/eitem.hxx>
95 #include <svl/lckbitem.hxx>
96 #include <svtools/sfxecode.hxx>
97 #include <svl/itemset.hxx>
98 #include <svl/intitem.hxx>
99 #include <svtools/svparser.hxx>
100 #include <cppuhelper/weakref.hxx>
101 #include <sal/log.hxx>
103 #include <unotools/streamwrap.hxx>
105 #include <osl/file.hxx>
107 #include <comphelper/storagehelper.hxx>
108 #include <unotools/mediadescriptor.hxx>
109 #include <comphelper/docpasswordhelper.hxx>
110 #include <tools/datetime.hxx>
111 #include <unotools/pathoptions.hxx>
112 #include <svtools/asynclink.hxx>
113 #include <ucbhelper/commandenvironment.hxx>
114 #include <unotools/ucbstreamhelper.hxx>
115 #include <unotools/ucbhelper.hxx>
116 #include <unotools/progresshandlerwrap.hxx>
117 #include <ucbhelper/content.hxx>
118 #include <ucbhelper/interactionrequest.hxx>
119 #include <sot/stg.hxx>
120 #include <sot/storage.hxx>
121 #include <unotools/saveopt.hxx>
122 #include <svl/documentlockfile.hxx>
123 #include <svl/msodocumentlockfile.hxx>
124 #include <com/sun/star/document/DocumentRevisionListPersistence.hpp>
126 #include <helper.hxx>
127 #include <sfx2/request.hxx>
128 #include <sfx2/app.hxx>
129 #include <sfx2/frame.hxx>
130 #include <sfx2/fcontnr.hxx>
131 #include <sfx2/docfilt.hxx>
132 #include <sfx2/objsh.hxx>
133 #include <sfx2/docfac.hxx>
134 #include <sfx2/sfxsids.hrc>
135 #include <sfx2/sfxuno.hxx>
136 #include <openflag.hxx>
137 #include <officecfg/Office/Common.hxx>
138 #include <comphelper/propertysequence.hxx>
139 #include <vcl/weld.hxx>
140 #include <vcl/svapp.hxx>
141 #include <tools/diagnose_ex.h>
142 #include <unotools/fltrcfg.hxx>
144 #include <com/sun/star/io/WrongFormatException.hpp>
146 #include <memory>
148 using namespace ::com::sun::star;
149 using namespace ::com::sun::star::graphic;
150 using namespace ::com::sun::star::uno;
151 using namespace ::com::sun::star::ucb;
152 using namespace ::com::sun::star::beans;
153 using namespace ::com::sun::star::io;
154 using namespace ::com::sun::star::security;
156 namespace {
158 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
160 bool IsSystemFileLockingUsed()
162 #if HAVE_FEATURE_MACOSX_SANDBOX
163 return true;
164 #else
165 return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
166 #endif
170 bool IsOOoLockFileUsed()
172 #if HAVE_FEATURE_MACOSX_SANDBOX
173 return false;
174 #else
175 return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get();
176 #endif
179 bool IsLockingUsed()
181 return officecfg::Office::Common::Misc::UseLocking::get();
184 #endif
186 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
187 bool IsWebDAVLockingUsed()
189 return officecfg::Office::Common::Misc::UseWebDAVFileLocking::get();
191 #endif
193 /// Gets default attributes of a file:// URL.
194 sal_uInt64 GetDefaultFileAttributes(const OUString& rURL)
196 sal_uInt64 nRet = 0;
198 if (!comphelper::isFileUrl(rURL))
199 return nRet;
201 // Make sure the file exists (and create it if not).
202 osl::File aFile(rURL);
203 osl::File::RC nRes = aFile.open(osl_File_OpenFlag_Create);
204 if (nRes != osl::File::E_None && nRes != osl::File::E_EXIST)
205 return nRet;
207 aFile.close();
209 osl::DirectoryItem aItem;
210 if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
211 return nRet;
213 osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
214 if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
215 return nRet;
217 nRet = aStatus.getAttributes();
218 return nRet;
221 /// Determines if rURL is safe to move or not.
222 bool IsFileMovable(const INetURLObject& rURL)
224 #ifdef MACOSX
225 (void)rURL;
226 // Hide extension macOS-specific file property would be lost.
227 return false;
228 #else
230 if (rURL.GetProtocol() != INetProtocol::File)
231 // Not a file:// URL.
232 return false;
234 #ifdef UNX
235 OUString sPath = rURL.getFSysPath(FSysStyle::Unix);
236 if (sPath.isEmpty())
237 return false;
239 struct stat buf;
240 if (lstat(sPath.toUtf8().getStr(), &buf) != 0)
241 return false;
243 // Hardlink or symlink: osl::File::move() doesn't play with these nicely.
244 if (buf.st_nlink > 1 || S_ISLNK(buf.st_mode))
245 return false;
246 #elif defined _WIN32
247 if (tools::IsMappedWebDAVPath(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)))
248 return false;
249 #endif
251 return true;
252 #endif
255 } // anonymous namespace
257 class SfxMedium_Impl
259 public:
260 StreamMode m_nStorOpenMode;
261 ErrCode m_eError;
263 ::ucbhelper::Content aContent;
264 bool bUpdatePickList:1;
265 bool bIsTemp:1;
266 bool bDownloadDone:1;
267 bool bIsStorage:1;
268 bool bUseInteractionHandler:1;
269 bool bAllowDefaultIntHdl:1;
270 bool bDisposeStorage:1;
271 bool bStorageBasedOnInStream:1;
272 bool m_bSalvageMode:1;
273 bool m_bVersionsAlreadyLoaded:1;
274 bool m_bLocked:1;
275 bool m_bMSOLockFileCreated : 1;
276 bool m_bDisableUnlockWebDAV:1;
277 bool m_bGotDateTime:1;
278 bool m_bRemoveBackup:1;
279 bool m_bOriginallyReadOnly:1;
280 bool m_bOriginallyLoadedReadOnly:1;
281 bool m_bTriedStorage:1;
282 bool m_bRemote:1;
283 bool m_bInputStreamIsReadOnly:1;
284 bool m_bInCheckIn:1;
285 bool m_bDisableFileSync = false;
287 OUString m_aName;
288 OUString m_aLogicName;
289 OUString m_aLongName;
291 mutable std::shared_ptr<SfxItemSet> m_pSet;
292 mutable std::unique_ptr<INetURLObject> m_pURLObj;
294 std::shared_ptr<const SfxFilter> m_pFilter;
295 std::shared_ptr<const SfxFilter> m_pCustomFilter;
297 std::unique_ptr<SvStream> m_pInStream;
298 std::unique_ptr<SvStream> m_pOutStream;
300 OUString aOrigURL;
301 DateTime aExpireTime;
302 SfxFrameWeakRef wLoadTargetFrame;
303 SvKeyValueIteratorRef xAttributes;
305 svtools::AsynchronLink aDoneLink;
307 uno::Sequence < util::RevisionTag > aVersions;
309 std::unique_ptr<::utl::TempFile> pTempFile;
311 uno::Reference<embed::XStorage> xStorage;
312 uno::Reference<embed::XStorage> m_xZipStorage;
313 uno::Reference<io::XInputStream> m_xInputStreamToLoadFrom;
314 uno::Reference<io::XInputStream> xInputStream;
315 uno::Reference<io::XStream> xStream;
316 uno::Reference<io::XStream> m_xLockingStream;
317 uno::Reference<task::XInteractionHandler> xInteraction;
319 ErrCode nLastStorageError;
321 OUString m_aBackupURL;
323 // the following member is changed and makes sense only during saving
324 // TODO/LATER: in future the signature state should be controlled by the medium not by the document
325 // in this case the member will hold this information
326 SignatureState m_nSignatureState;
328 bool m_bHasEmbeddedObjects = false;
330 util::DateTime m_aDateTime;
332 explicit SfxMedium_Impl();
333 ~SfxMedium_Impl();
334 SfxMedium_Impl(const SfxMedium_Impl&) = delete;
335 SfxMedium_Impl& operator=(const SfxMedium_Impl&) = delete;
337 OUString getFilterMimeType() const
338 { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); }
342 SfxMedium_Impl::SfxMedium_Impl() :
343 m_nStorOpenMode(SFX_STREAM_READWRITE),
344 m_eError(ERRCODE_NONE),
345 bUpdatePickList(true),
346 bIsTemp( false ),
347 bDownloadDone( true ),
348 bIsStorage( false ),
349 bUseInteractionHandler( true ),
350 bAllowDefaultIntHdl( false ),
351 bDisposeStorage( false ),
352 bStorageBasedOnInStream( false ),
353 m_bSalvageMode( false ),
354 m_bVersionsAlreadyLoaded( false ),
355 m_bLocked( false ),
356 m_bMSOLockFileCreated( false ),
357 m_bDisableUnlockWebDAV( false ),
358 m_bGotDateTime( false ),
359 m_bRemoveBackup( false ),
360 m_bOriginallyReadOnly(false),
361 m_bOriginallyLoadedReadOnly(false),
362 m_bTriedStorage(false),
363 m_bRemote(false),
364 m_bInputStreamIsReadOnly(false),
365 m_bInCheckIn(false),
366 aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ),
367 nLastStorageError( ERRCODE_NONE ),
368 m_nSignatureState( SignatureState::NOSIGNATURES )
370 aDoneLink.CreateMutex();
374 SfxMedium_Impl::~SfxMedium_Impl()
376 aDoneLink.ClearPendingCall();
378 pTempFile.reset();
379 m_pSet.reset();
380 m_pURLObj.reset();
383 void SfxMedium::ResetError()
385 pImpl->m_eError = ERRCODE_NONE;
386 if( pImpl->m_pInStream )
387 pImpl->m_pInStream->ResetError();
388 if( pImpl->m_pOutStream )
389 pImpl->m_pOutStream->ResetError();
392 ErrCode const & SfxMedium::GetLastStorageCreationState() const
394 return pImpl->nLastStorageError;
397 void SfxMedium::SetError(ErrCode nError)
399 pImpl->m_eError = nError;
402 ErrCode SfxMedium::GetErrorCode() const
404 ErrCode lError = pImpl->m_eError;
405 if(!lError && pImpl->m_pInStream)
406 lError = pImpl->m_pInStream->GetErrorCode();
407 if(!lError && pImpl->m_pOutStream)
408 lError = pImpl->m_pOutStream->GetErrorCode();
409 return lError;
412 void SfxMedium::CheckFileDate( const util::DateTime& aInitDate )
414 GetInitFileDate( true );
415 if ( pImpl->m_aDateTime.Seconds == aInitDate.Seconds
416 && pImpl->m_aDateTime.Minutes == aInitDate.Minutes
417 && pImpl->m_aDateTime.Hours == aInitDate.Hours
418 && pImpl->m_aDateTime.Day == aInitDate.Day
419 && pImpl->m_aDateTime.Month == aInitDate.Month
420 && pImpl->m_aDateTime.Year == aInitDate.Year )
421 return;
423 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
425 if ( !xHandler.is() )
426 return;
430 ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
431 document::ChangedByOthersRequest() ) );
432 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( 3 );
433 aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
434 aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
435 xInteractionRequestImpl->setContinuations( aContinuations );
437 xHandler->handle( xInteractionRequestImpl.get() );
439 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
440 if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
442 SetError(ERRCODE_ABORT);
445 catch ( const uno::Exception& )
449 bool SfxMedium::DocNeedsFileDateCheck() const
451 return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File ||
452 GetURLObject().isAnyKnownWebDAVScheme() ) );
455 util::DateTime const & SfxMedium::GetInitFileDate( bool bIgnoreOldValue )
457 if ( ( bIgnoreOldValue || !pImpl->m_bGotDateTime ) && !pImpl->m_aLogicName.isEmpty() )
461 // add a default css::ucb::XCommandEnvironment
462 // in order to have the WebDAV UCP provider manage http/https authentication correctly
463 ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
464 utl::UCBContentHelper::getDefaultCommandEnvironment(),
465 comphelper::getProcessComponentContext() );
467 aContent.getPropertyValue("DateModified") >>= pImpl->m_aDateTime;
468 pImpl->m_bGotDateTime = true;
470 catch ( const css::uno::Exception& )
475 return pImpl->m_aDateTime;
479 Reference < XContent > SfxMedium::GetContent() const
481 if ( !pImpl->aContent.get().is() )
483 Reference < css::ucb::XContent > xContent;
485 // tdf#95144 add a default css::ucb::XCommandEnvironment
486 // in order to have the WebDAV UCP provider manage https protocol certificates correctly
487 css:: uno::Reference< task::XInteractionHandler > xIH(
488 css::task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr ) );
490 css::uno::Reference< css::ucb::XProgressHandler > xProgress;
491 ::ucbhelper::CommandEnvironment* pCommandEnv = new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
493 Reference < css::ucb::XCommandEnvironment > xEnv( static_cast< css::ucb::XCommandEnvironment* >(pCommandEnv), css::uno::UNO_QUERY );
495 const SfxUnoAnyItem* pItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_CONTENT, false);
496 if ( pItem )
497 pItem->GetValue() >>= xContent;
499 if ( xContent.is() )
503 pImpl->aContent = ::ucbhelper::Content( xContent, xEnv, comphelper::getProcessComponentContext() );
505 catch ( const Exception& )
509 else
511 // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's used.");
512 OUString aURL;
513 if ( !pImpl->m_aName.isEmpty() )
514 osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL );
515 else if ( !pImpl->m_aLogicName.isEmpty() )
516 aURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
517 if (!aURL.isEmpty() )
518 (void)::ucbhelper::Content::create( aURL, xEnv, comphelper::getProcessComponentContext(), pImpl->aContent );
522 return pImpl->aContent.get();
525 OUString SfxMedium::GetBaseURL( bool bForSaving )
527 OUString aBaseURL;
528 const SfxStringItem* pBaseURLItem = GetItemSet()->GetItem<SfxStringItem>(SID_DOC_BASEURL);
529 if ( pBaseURLItem )
530 aBaseURL = pBaseURLItem->GetValue();
531 else if (!utl::ConfigManager::IsFuzzing() && GetContent().is())
535 Any aAny = pImpl->aContent.getPropertyValue("BaseURI");
536 aAny >>= aBaseURL;
538 catch ( const css::uno::Exception& )
542 if ( aBaseURL.isEmpty() )
543 aBaseURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
546 if ( bForSaving )
548 SvtSaveOptions aOpt;
549 bool bIsRemote = IsRemote();
550 if( (bIsRemote && !aOpt.IsSaveRelINet()) || (!pImpl->m_bRemote && !aOpt.IsSaveRelFSys()) )
551 return OUString();
554 return aBaseURL;
557 bool SfxMedium::IsSkipImages() const
559 const SfxStringItem* pSkipImagesItem = GetItemSet()->GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS);
560 return pSkipImagesItem && pSkipImagesItem->GetValue() == "SkipImages";
563 SvStream* SfxMedium::GetInStream()
565 if ( pImpl->m_pInStream )
566 return pImpl->m_pInStream.get();
568 if ( pImpl->pTempFile )
570 pImpl->m_pInStream.reset( new SvFileStream(pImpl->m_aName, pImpl->m_nStorOpenMode) );
572 pImpl->m_eError = pImpl->m_pInStream->GetError();
574 if (!pImpl->m_eError && (pImpl->m_nStorOpenMode & StreamMode::WRITE)
575 && ! pImpl->m_pInStream->IsWritable() )
577 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
578 pImpl->m_pInStream.reset();
580 else
581 return pImpl->m_pInStream.get();
584 GetMedium_Impl();
586 if ( GetError() )
587 return nullptr;
589 return pImpl->m_pInStream.get();
593 void SfxMedium::CloseInStream()
595 CloseInStream_Impl();
598 void SfxMedium::CloseInStream_Impl(bool bInDestruction)
600 // if there is a storage based on the InStream, we have to
601 // close the storage, too, because otherwise the storage
602 // would use an invalid ( deleted ) stream.
603 if ( pImpl->m_pInStream && pImpl->xStorage.is() )
605 if ( pImpl->bStorageBasedOnInStream )
606 CloseStorage();
609 if ( pImpl->m_pInStream && !GetContent().is() && !bInDestruction )
611 CreateTempFile();
612 return;
615 pImpl->m_pInStream.reset();
616 if ( pImpl->m_pSet )
617 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
619 CloseZipStorage_Impl();
620 pImpl->xInputStream.clear();
622 if ( !pImpl->m_pOutStream )
624 // output part of the stream is not used so the whole stream can be closed
625 // TODO/LATER: is it correct?
626 pImpl->xStream.clear();
627 if ( pImpl->m_pSet )
628 pImpl->m_pSet->ClearItem( SID_STREAM );
633 SvStream* SfxMedium::GetOutStream()
635 if ( !pImpl->m_pOutStream )
637 // Create a temp. file if there is none because we always
638 // need one.
639 CreateTempFile( false );
641 if ( pImpl->pTempFile )
643 // On windows we try to re-use XOutStream from xStream if that exists;
644 // because opening new SvFileStream in this situation may fail with ERROR_SHARING_VIOLATION
645 // TODO: this is a horrible hack that should probably be removed,
646 // somebody needs to investigate this more thoroughly...
647 if (getenv("SFX_MEDIUM_REUSE_STREAM") && pImpl->xStream.is())
649 assert(pImpl->xStream->getOutputStream().is()); // need that...
650 pImpl->m_pOutStream = utl::UcbStreamHelper::CreateStream(
651 pImpl->xStream, false);
653 else
655 // On Unix don't try to re-use XOutStream from xStream if that exists;
656 // it causes fdo#59022 (fails opening files via SMB on Linux)
657 pImpl->m_pOutStream.reset( new SvFileStream(
658 pImpl->m_aName, StreamMode::STD_READWRITE) );
660 CloseStorage();
664 return pImpl->m_pOutStream.get();
668 void SfxMedium::CloseOutStream()
670 CloseOutStream_Impl();
673 void SfxMedium::CloseOutStream_Impl()
675 if ( pImpl->m_pOutStream )
677 // if there is a storage based on the OutStream, we have to
678 // close the storage, too, because otherwise the storage
679 // would use an invalid ( deleted ) stream.
680 //TODO/MBA: how to deal with this?!
681 //maybe we need a new flag when the storage was created from the outstream
682 if ( pImpl->xStorage.is() )
684 CloseStorage();
687 pImpl->m_pOutStream.reset();
690 if ( !pImpl->m_pInStream )
692 // input part of the stream is not used so the whole stream can be closed
693 // TODO/LATER: is it correct?
694 pImpl->xStream.clear();
695 if ( pImpl->m_pSet )
696 pImpl->m_pSet->ClearItem( SID_STREAM );
701 const OUString& SfxMedium::GetPhysicalName() const
703 if ( pImpl->m_aName.isEmpty() && !pImpl->m_aLogicName.isEmpty() )
704 const_cast<SfxMedium*>(this)->CreateFileStream();
706 // return the name then
707 return pImpl->m_aName;
711 void SfxMedium::CreateFileStream()
713 // force synchron
714 if( pImpl->m_pInStream )
716 SvLockBytes* pBytes = pImpl->m_pInStream->GetLockBytes();
717 if( pBytes )
718 pBytes->SetSynchronMode();
721 GetInStream();
722 if( pImpl->m_pInStream )
724 CreateTempFile( false );
725 pImpl->bIsTemp = true;
726 CloseInStream_Impl();
731 bool SfxMedium::Commit()
733 if( pImpl->xStorage.is() )
734 StorageCommit_Impl();
735 else if( pImpl->m_pOutStream )
736 pImpl->m_pOutStream->Flush();
737 else if( pImpl->m_pInStream )
738 pImpl->m_pInStream->Flush();
740 if ( GetError() == ERRCODE_NONE )
742 // does something only in case there is a temporary file ( means aName points to different location than aLogicName )
743 Transfer_Impl();
746 bool bResult = ( GetError() == ERRCODE_NONE );
748 if ( bResult && DocNeedsFileDateCheck() )
749 GetInitFileDate( true );
751 // remove truncation mode from the flags
752 pImpl->m_nStorOpenMode &= ~StreamMode::TRUNC;
753 return bResult;
757 bool SfxMedium::IsStorage()
759 if ( pImpl->xStorage.is() )
760 return true;
762 if ( pImpl->m_bTriedStorage )
763 return pImpl->bIsStorage;
765 if ( pImpl->pTempFile )
767 OUString aURL;
768 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL )
769 != osl::FileBase::E_None )
771 SAL_WARN( "sfx.doc", "Physical name '" << pImpl->m_aName << "' not convertible to file URL");
773 pImpl->bIsStorage = SotStorage::IsStorageFile( aURL ) && !SotStorage::IsOLEStorage( aURL);
774 if ( !pImpl->bIsStorage )
775 pImpl->m_bTriedStorage = true;
777 else if ( GetInStream() )
779 pImpl->bIsStorage = SotStorage::IsStorageFile( pImpl->m_pInStream.get() ) && !SotStorage::IsOLEStorage( pImpl->m_pInStream.get() );
780 if ( !pImpl->m_pInStream->GetError() && !pImpl->bIsStorage )
781 pImpl->m_bTriedStorage = true;
784 return pImpl->bIsStorage;
788 bool SfxMedium::IsPreview_Impl() const
790 bool bPreview = false;
791 const SfxBoolItem* pPreview = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_PREVIEW, false);
792 if ( pPreview )
793 bPreview = pPreview->GetValue();
794 else
796 const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_OPTIONS, false);
797 if ( pFlags )
799 OUString aFileFlags = pFlags->GetValue();
800 aFileFlags = aFileFlags.toAsciiUpperCase();
801 if ( -1 != aFileFlags.indexOf( 'B' ) )
802 bPreview = true;
806 return bPreview;
810 void SfxMedium::StorageBackup_Impl()
812 ::ucbhelper::Content aOriginalContent;
813 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
815 bool bBasedOnOriginalFile = ( !pImpl->pTempFile && !( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
816 && !GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty()
817 && GetURLObject().GetProtocol() == INetProtocol::File
818 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
820 if ( bBasedOnOriginalFile && pImpl->m_aBackupURL.isEmpty()
821 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aOriginalContent ) )
823 DoInternalBackup_Impl( aOriginalContent );
824 if( pImpl->m_aBackupURL.isEmpty() )
825 SetError(ERRCODE_SFX_CANTCREATEBACKUP);
830 OUString const & SfxMedium::GetBackup_Impl()
832 if ( pImpl->m_aBackupURL.isEmpty() )
833 StorageBackup_Impl();
835 return pImpl->m_aBackupURL;
839 uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage()
841 if ( GetError() )
842 return uno::Reference< embed::XStorage >();
844 // if the medium was constructed with a Storage: use this one, not a temp. storage
845 // if a temporary storage already exists: use it
846 if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile ) )
847 return pImpl->xStorage;
849 // if necessary close stream that was used for reading
850 if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() )
851 CloseInStream();
853 DBG_ASSERT( !pImpl->m_pOutStream, "OutStream in a readonly Medium?!" );
855 // TODO/LATER: The current solution is to store the document temporary and then copy it to the target location;
856 // in future it should be stored directly and then copied to the temporary location, since in this case no
857 // file attributes have to be preserved and system copying mechanics could be used instead of streaming.
858 CreateTempFileNoCopy();
860 return GetStorage();
864 void SfxMedium::SetEncryptionDataToStorage_Impl()
866 // in case media-descriptor contains password it should be used on opening
867 if ( !pImpl->xStorage.is() || !pImpl->m_pSet )
868 return;
870 uno::Sequence< beans::NamedValue > aEncryptionData;
871 if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
872 return;
874 // replace the password with encryption data
875 pImpl->m_pSet->ClearItem( SID_PASSWORD );
876 pImpl->m_pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) );
880 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl->xStorage, aEncryptionData );
882 catch( const uno::Exception& )
884 SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" );
885 // TODO/LATER: set the error code in case of problem
886 // SetError(ERRCODE_IO_GENERAL);
890 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
892 // FIXME: Hmm actually lock files should be used for sftp: documents
893 // even if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT. Only the use of lock
894 // files for *local* documents is unnecessary in that case. But
895 // actually, the checks for sftp: here are just wishful thinking; I
896 // don't this there is any support for actually editing documents
897 // behind sftp: URLs anyway.
899 // Sure, there could perhaps be a 3rd-party extension that brings UCB
900 // the potential to handle files behind sftp:. But there could also be
901 // an extension that handles some arbitrary foobar: scheme *and* it
902 // could be that lock files would be the correct thing to use for
903 // foobar: documents, too. But the hardcoded test below won't know
904 // that. Clearly the knowledge whether lock files should be used or
905 // not for some URL scheme belongs in UCB, not here.
907 namespace
910 OUString tryMSOwnerFiles(const OUString& sDocURL)
912 svt::MSODocumentLockFile aMSOLockFile(sDocURL);
913 LockFileEntry aData;
916 aData = aMSOLockFile.GetLockData();
918 catch( const uno::Exception& )
920 return OUString();
923 OUString sUserData = aData[LockFileComponent::OOOUSERNAME];
925 if (!sUserData.isEmpty())
926 sUserData += " (MS Office)"; // Mention the used office suite
928 return sUserData;
931 OUString tryForeignLockfiles(const OUString& sDocURL)
933 OUString sUserData = tryMSOwnerFiles(sDocURL);
934 // here we can test for empty result, and add other known applications' lockfile testing
935 return sUserData.trim();
939 SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntry& aData,
940 bool bIsLoading, bool bOwnLock,
941 bool bHandleSysLocked)
943 ShowLockResult nResult = ShowLockResult::NoLock;
945 // tdf#92817: Simple check for empty lock file that needs to be deleted, when system locking is enabled
946 if( aData[LockFileComponent::OOOUSERNAME].isEmpty() && aData[LockFileComponent::SYSUSERNAME].isEmpty() && !bHandleSysLocked )
947 bOwnLock=true;
949 // show the interaction regarding the document opening
950 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
952 if ( xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
954 OUString aDocumentURL
955 = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset);
956 OUString aInfo;
957 ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl;
959 sal_Int32 nContinuations = 3;
961 if ( bOwnLock )
963 aInfo = aData[LockFileComponent::EDITTIME];
965 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
966 document::OwnLockOnDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo, !bIsLoading ) ) );
968 else
970 // Use a fourth continuation in case there's no filesystem lock:
971 // "Ignore lock file and open/replace the document"
972 if (!bHandleSysLocked)
973 nContinuations = 4;
975 if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
976 aInfo = aData[LockFileComponent::OOOUSERNAME];
977 else
978 aInfo = aData[LockFileComponent::SYSUSERNAME];
980 if (aInfo.isEmpty() && !GetURLObject().isAnyKnownWebDAVScheme())
981 // Try to get name of user who has locked the file using other applications
982 aInfo = tryForeignLockfiles(
983 GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
985 if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() )
986 aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )";
988 if (!bIsLoading) // so, !bHandleSysLocked
990 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny(
991 document::LockedOnSavingRequest(OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo)));
992 // Currently, only the last "Retry" continuation (meaning ignore the lock and try overwriting) can be returned.
994 else /*logically therefore bIsLoading is set */
996 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
997 document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) );
1001 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations);
1002 aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
1003 aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
1004 aContinuations[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl.get() );
1005 if (nContinuations > 3)
1007 // We use InteractionRetry to reflect that user wants to
1008 // ignore the (stale?) alien lock file and open/overwrite the document
1009 aContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get());
1011 xInteractionRequestImpl->setContinuations( aContinuations );
1013 xHandler->handle( xInteractionRequestImpl.get() );
1015 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
1016 if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
1018 SetError(ERRCODE_ABORT);
1020 else if ( uno::Reference< task::XInteractionDisapprove >( xSelected.get(), uno::UNO_QUERY ).is() )
1022 // own lock on loading, user has selected to ignore the lock
1023 // own lock on saving, user has selected to ignore the lock
1024 // alien lock on loading, user has selected to edit a copy of document
1025 // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location
1026 if ( !bOwnLock ) // bIsLoading implied from outermost condition
1028 // means that a copy of the document should be opened
1029 GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) );
1031 else
1032 nResult = ShowLockResult::Succeeded;
1034 else if (uno::Reference< task::XInteractionRetry >(xSelected.get(), uno::UNO_QUERY).is())
1036 // User decided to ignore the alien (stale?) lock file without filesystem lock
1037 nResult = ShowLockResult::Succeeded;
1039 else // if ( XSelected == aContinuations[1] )
1041 // own lock on loading, user has selected to open readonly
1042 // own lock on saving, user has selected to open readonly
1043 // alien lock on loading, user has selected to retry saving
1044 // TODO/LATER: alien lock on saving, user has selected to retry saving
1046 if ( bIsLoading )
1047 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1048 else
1049 nResult = ShowLockResult::Try;
1052 else
1054 if ( bIsLoading )
1056 // if no interaction handler is provided the default answer is open readonly
1057 // that usually happens in case the document is loaded per API
1058 // so the document must be opened readonly for backward compatibility
1059 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1061 else
1062 SetError(ERRCODE_IO_ACCESSDENIED);
1066 return nResult;
1070 bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg)
1072 // system file locking is not active, ask user whether he wants to open the document without any locking
1073 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
1075 if (xHandler.is())
1077 ::rtl::Reference< ::ucbhelper::InteractionRequest > xIgnoreRequestImpl;
1079 switch (nWhichDlg)
1081 case MessageDlg::LockFileIgnore:
1082 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileIgnoreRequest() ));
1083 break;
1084 case MessageDlg::LockFileCorrupt:
1085 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileCorruptRequest() ));
1086 break;
1089 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(2);
1090 aContinuations[0] = new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl.get());
1091 aContinuations[1] = new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl.get());
1092 xIgnoreRequestImpl->setContinuations(aContinuations);
1094 xHandler->handle(xIgnoreRequestImpl.get());
1096 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection();
1097 bool bReadOnly = uno::Reference< task::XInteractionApprove >(xSelected.get(), uno::UNO_QUERY).is();
1099 if (bReadOnly)
1101 GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
1103 else
1105 SetError(ERRCODE_ABORT);
1108 return bReadOnly;
1111 return false;
1114 namespace
1116 bool isSuitableProtocolForLocking(const OUString & rLogicName)
1118 INetURLObject aUrl( rLogicName );
1119 INetProtocol eProt = aUrl.GetProtocol();
1120 #if !HAVE_FEATURE_MACOSX_SANDBOX
1121 if (eProt == INetProtocol::File) {
1122 return true;
1124 #endif
1125 return eProt == INetProtocol::Smb || eProt == INetProtocol::Sftp;
1129 namespace
1132 // for LOCK request, suppress dialog on 403, typically indicates read-only
1133 // document and there's a 2nd dialog prompting to open a copy anyway
1134 class LockInteractionHandler : public ::cppu::WeakImplHelper<task::XInteractionHandler>
1136 private:
1137 uno::Reference<task::XInteractionHandler> m_xHandler;
1139 public:
1140 explicit LockInteractionHandler(uno::Reference<task::XInteractionHandler> const& xHandler)
1141 : m_xHandler(xHandler)
1145 virtual void SAL_CALL handle(uno::Reference<task::XInteractionRequest> const& xRequest) override
1147 ucb::InteractiveNetworkWriteException readException;
1148 ucb::InteractiveNetworkReadException writeException;
1149 if ((xRequest->getRequest() >>= readException)
1150 || (xRequest->getRequest() >>= writeException))
1152 return; // 403 gets reported as one of these; ignore to avoid dialog
1154 m_xHandler->handle(xRequest);
1158 } // namespace
1160 #endif // HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1162 // sets SID_DOC_READONLY if the document cannot be opened for editing
1163 // if user cancel the loading the ERROR_ABORT is set
1164 SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bNoUI,
1165 bool bTryIgnoreLockFile,
1166 LockFileEntry* pLockData)
1168 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1169 (void) bLoading;
1170 (void) bNoUI;
1171 (void) bTryIgnoreLockFile;
1172 (void) pLockData;
1173 return LockFileResult::Succeeded;
1174 #else
1175 LockFileResult eResult = LockFileResult::Failed;
1177 // check if path scheme is http:// or https://
1178 // may be this is better if used always, in Android and iOS as well?
1179 // if this code should be always there, remember to move the relevant code in UnlockFile method as well !
1181 if ( GetURLObject().isAnyKnownWebDAVScheme() )
1183 // do nothing if WebDAV locking is disabled
1184 if (!IsWebDAVLockingUsed())
1185 return LockFileResult::Succeeded;
1189 bool bResult = pImpl->m_bLocked;
1190 bool bIsTemplate = false;
1191 // so, this is webdav stuff...
1192 if ( !bResult )
1194 // no read-write access is necessary on loading if the document is explicitly opened as copy
1195 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
1196 bIsTemplate = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
1199 if ( !bIsTemplate && !bResult && !IsReadOnly() )
1201 ShowLockResult bUIStatus = ShowLockResult::NoLock;
1204 if( !bResult )
1206 uno::Reference< task::XInteractionHandler > xCHandler = GetInteractionHandler( true );
1207 // Dialog with error is superfluous:
1208 // on loading, will result in read-only with infobar.
1209 // bNoUI case for Reload failing, will open dialog later.
1210 if (bLoading || bNoUI)
1212 xCHandler = new LockInteractionHandler(xCHandler);
1214 Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment(
1215 xCHandler, Reference< css::ucb::XProgressHandler >() );
1217 ucbhelper::Content aContentToLock(
1218 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1219 xComEnv, comphelper::getProcessComponentContext() );
1223 aContentToLock.lock();
1224 bResult = true;
1226 catch ( ucb::InteractiveLockingLockedException& )
1228 // received when the resource is already locked
1229 if (!bNoUI || pLockData)
1231 // get the lock owner, using a special ucb.webdav property
1232 // the owner property retrieved here is what the other principal send the server
1233 // when activating the lock.
1234 // See http://tools.ietf.org/html/rfc4918#section-14.17 for details
1235 LockFileEntry aLockData;
1236 aLockData[LockFileComponent::OOOUSERNAME] = "Unknown user";
1237 // This solution works right when the LO user name and the WebDAV user
1238 // name are the same.
1239 // A better thing to do would be to obtain the 'real' WebDAV user name,
1240 // but that's not possible from a WebDAV UCP provider client.
1241 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
1242 // use the current LO user name as the system name
1243 aLockData[LockFileComponent::SYSUSERNAME]
1244 = aOwnData[LockFileComponent::SYSUSERNAME];
1246 uno::Sequence<css::ucb::Lock> aLocks;
1247 // getting the property, send a PROPFIND to the server over the net
1248 if ((aContentToLock.getPropertyValue("DAV:lockdiscovery") >>= aLocks) && aLocks.hasElements())
1250 // got at least a lock, show the owner of the first lock returned
1251 css::ucb::Lock aLock = aLocks[0];
1252 OUString aOwner;
1253 if (aLock.Owner >>= aOwner)
1255 // we need to display the WebDAV user name owning the lock, not the local one
1256 aLockData[LockFileComponent::OOOUSERNAME] = aOwner;
1260 if (!bNoUI)
1262 bUIStatus = ShowLockedDocumentDialog(aLockData, bLoading, false,
1263 true);
1266 if (pLockData)
1268 std::copy(aLockData.begin(), aLockData.end(), pLockData->begin());
1272 catch( ucb::InteractiveNetworkWriteException& )
1274 // This catch it's not really needed, here just for the sake of documentation on the behaviour.
1275 // This is the most likely reason:
1276 // - the remote site is a WebDAV with special configuration: read/only for read operations
1277 // and read/write for write operations, the user is not allowed to lock/write and
1278 // she cancelled the credentials request.
1279 // this is not actually an error, but the exception is sent directly from ucb, avoiding the automatic
1280 // management that takes part in cancelCommandExecution()
1281 // Unfortunately there is no InteractiveNetwork*Exception available to signal this more correctly
1282 // since it mostly happens on read/only part of webdav, this can be the most correct
1283 // exception available
1285 catch( uno::Exception& )
1288 } while( !bResult && bUIStatus == ShowLockResult::Try );
1291 pImpl->m_bLocked = bResult;
1293 if ( !bResult && GetError() == ERRCODE_NONE )
1295 // the error should be set in case it is storing process
1296 // or the document has been opened for editing explicitly
1297 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
1299 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
1300 SetError(ERRCODE_IO_ACCESSDENIED);
1301 else
1302 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1305 // when the file is locked, get the current file date
1306 if ( bResult && DocNeedsFileDateCheck() )
1307 GetInitFileDate( true );
1309 if ( bResult )
1310 eResult = LockFileResult::Succeeded;
1312 catch ( const uno::Exception& )
1314 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
1316 return eResult;
1319 if (!IsLockingUsed())
1320 return LockFileResult::Succeeded;
1321 if (GetURLObject().HasError())
1322 return eResult;
1326 if ( pImpl->m_bLocked && bLoading
1327 && GetURLObject().GetProtocol() == INetProtocol::File )
1329 // if the document is already locked the system locking might be temporarily off after storing
1330 // check whether the system file locking should be taken again
1331 GetLockingStream_Impl();
1334 bool bResult = pImpl->m_bLocked;
1336 if ( !bResult )
1338 // no read-write access is necessary on loading if the document is explicitly opened as copy
1339 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
1340 bResult = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
1343 if ( !bResult && !IsReadOnly() )
1345 bool bContentReadonly = false;
1346 if ( bLoading && GetURLObject().GetProtocol() == INetProtocol::File )
1348 // let the original document be opened to check the possibility to open it for editing
1349 // and to let the writable stream stay open to hold the lock on the document
1350 GetLockingStream_Impl();
1353 // "IsReadOnly" property does not allow to detect whether the file is readonly always
1354 // so we try always to open the file for editing
1355 // the file is readonly only in case the read-write stream can not be opened
1356 if ( bLoading && !pImpl->m_xLockingStream.is() )
1360 // MediaDescriptor does this check also, the duplication should be avoided in future
1361 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1362 ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
1363 aContent.getPropertyValue("IsReadOnly") >>= bContentReadonly;
1365 catch( const uno::Exception& ) {}
1368 // do further checks only if the file not readonly in fs
1369 if ( !bContentReadonly )
1371 // the special file locking should be used only for suitable URLs
1372 if ( isSuitableProtocolForLocking( pImpl->m_aLogicName ) )
1375 // in case of storing the document should request the output before locking
1376 if ( bLoading )
1378 // let the stream be opened to check the system file locking
1379 GetMedium_Impl();
1380 if (GetError() != ERRCODE_NONE) {
1381 return eResult;
1385 ShowLockResult bUIStatus = ShowLockResult::NoLock;
1387 // check whether system file locking has been used, the default value is false
1388 bool bUseSystemLock = comphelper::isFileUrl( pImpl->m_aLogicName ) && IsSystemFileLockingUsed();
1390 // TODO/LATER: This implementation does not allow to detect the system lock on saving here, actually this is no big problem
1391 // if system lock is used the writeable stream should be available
1392 bool bHandleSysLocked = ( bLoading && bUseSystemLock && !pImpl->xStream.is() && !pImpl->m_pOutStream );
1394 // The file is attempted to get locked for the duration of lockfile creation on save
1395 std::unique_ptr<osl::File> pFileLock;
1396 if (!bLoading && bUseSystemLock && pImpl->pTempFile)
1398 INetURLObject aDest(GetURLObject());
1399 OUString aDestURL(aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1401 if (comphelper::isFileUrl(aDestURL) || !aDest.removeSegment())
1403 pFileLock = std::make_unique<osl::File>(aDestURL);
1404 auto rc = pFileLock->open(osl_File_OpenFlag_Write);
1405 if (rc == osl::FileBase::E_ACCES)
1406 bHandleSysLocked = true;
1414 ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
1416 std::unique_ptr<svt::MSODocumentLockFile> pMSOLockFile;
1417 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
1418 if (rOpt.IsMSOLockFileCreationIsEnabled() && svt::MSODocumentLockFile::IsMSOSupportedFileFormat(pImpl->m_aLogicName))
1420 pMSOLockFile.reset(new svt::MSODocumentLockFile(pImpl->m_aLogicName));
1421 pImpl->m_bMSOLockFileCreated = true;
1424 bool bIoErr = false;
1426 if (!bHandleSysLocked)
1430 bResult = aLockFile.CreateOwnLockFile();
1431 if(pMSOLockFile)
1432 bResult &= pMSOLockFile->CreateOwnLockFile();
1434 catch (const uno::Exception&)
1436 if (tools::IsMappedWebDAVPath(GetURLObject().GetMainURL(
1437 INetURLObject::DecodeMechanism::NONE)))
1439 // This is a path that redirects to a WebDAV resource;
1440 // so failure creating lockfile is not an error here.
1441 bResult = true;
1443 else if (bLoading && !bNoUI)
1445 bIoErr = true;
1446 ShowLockFileProblemDialog(MessageDlg::LockFileIgnore);
1447 bResult = true; // always delete the defect lock-file
1451 // in case OOo locking is turned off the lock file is still written if possible
1452 // but it is ignored while deciding whether the document should be opened for editing or not
1453 if (!bResult && !IsOOoLockFileUsed() && !bIoErr)
1455 bResult = true;
1456 // take the ownership over the lock file
1457 aLockFile.OverwriteOwnLockFile();
1459 if(pMSOLockFile)
1460 pMSOLockFile->OverwriteOwnLockFile();
1464 if ( !bResult )
1466 LockFileEntry aData;
1469 aData = aLockFile.GetLockData();
1471 catch (const io::WrongFormatException&)
1473 // we get empty or corrupt data
1474 // info to the user
1475 if (!bIoErr && bLoading && !bNoUI )
1476 bResult = ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt);
1478 // not show the Lock Document Dialog
1479 bIoErr = true;
1481 catch( const uno::Exception& )
1483 // show the Lock Document Dialog, when locked from other app
1484 bIoErr = !bHandleSysLocked;
1487 bool bOwnLock = false;
1489 if (!bHandleSysLocked)
1491 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
1492 bOwnLock = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME];
1494 if (bOwnLock
1495 && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST]
1496 && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL])
1498 // this is own lock from the same installation, it could remain because of crash
1499 bResult = true;
1503 if ( !bResult && !bIoErr)
1505 if (!bNoUI)
1506 bUIStatus = ShowLockedDocumentDialog(
1507 aData, bLoading, bOwnLock, bHandleSysLocked);
1508 else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked)
1509 bUIStatus = ShowLockResult::Succeeded;
1511 if ( bUIStatus == ShowLockResult::Succeeded )
1513 // take the ownership over the lock file
1514 bResult = aLockFile.OverwriteOwnLockFile();
1516 if(pMSOLockFile)
1517 pMSOLockFile->OverwriteOwnLockFile();
1519 else if (bLoading && !bHandleSysLocked)
1520 eResult = LockFileResult::FailedLockFile;
1522 if (!bResult && pLockData)
1524 std::copy(aData.begin(), aData.end(), pLockData->begin());
1529 catch( const uno::Exception& )
1532 } while( !bResult && bUIStatus == ShowLockResult::Try );
1534 pImpl->m_bLocked = bResult;
1536 else
1538 // this is no file URL, check whether the file is readonly
1539 bResult = !bContentReadonly;
1544 if ( !bResult && GetError() == ERRCODE_NONE )
1546 // the error should be set in case it is storing process
1547 // or the document has been opened for editing explicitly
1548 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
1550 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
1551 SetError(ERRCODE_IO_ACCESSDENIED);
1552 else
1553 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1556 // when the file is locked, get the current file date
1557 if ( bResult && DocNeedsFileDateCheck() )
1558 GetInitFileDate( true );
1560 if ( bResult )
1561 eResult = LockFileResult::Succeeded;
1563 catch( const uno::Exception& )
1565 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: high probability, that the content has not been created" );
1568 return eResult;
1569 #endif
1573 uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
1575 if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage )
1576 return pImpl->xStorage;
1578 uno::Sequence< uno::Any > aArgs( 2 );
1580 // the medium should be retrieved before temporary file creation
1581 // to let the MediaDescriptor be filled with the streams
1582 GetMedium_Impl();
1584 if ( bCreateTempFile )
1585 CreateTempFile( false );
1587 GetMedium_Impl();
1589 if ( GetError() )
1590 return pImpl->xStorage;
1592 const SfxBoolItem* pRepairItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_REPAIRPACKAGE, false);
1593 if ( pRepairItem && pRepairItem->GetValue() )
1595 // the storage should be created for repairing mode
1596 CreateTempFile( false );
1597 GetMedium_Impl();
1599 Reference< css::ucb::XProgressHandler > xProgressHandler;
1600 Reference< css::task::XStatusIndicator > xStatusIndicator;
1602 const SfxUnoAnyItem* pxProgressItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetItemSet(), SID_PROGRESS_STATUSBAR_CONTROL, false);
1603 if( pxProgressItem && ( pxProgressItem->GetValue() >>= xStatusIndicator ) )
1604 xProgressHandler.set( new utl::ProgressHandlerWrap( xStatusIndicator ) );
1606 uno::Sequence< beans::PropertyValue > aAddProps( 2 );
1607 aAddProps[0].Name = "RepairPackage";
1608 aAddProps[0].Value <<= true;
1609 aAddProps[1].Name = "StatusIndicator";
1610 aAddProps[1].Value <<= xProgressHandler;
1612 // the first arguments will be filled later
1613 aArgs.realloc( 3 );
1614 aArgs[2] <<= aAddProps;
1617 if ( pImpl->xStream.is() )
1619 // since the storage is based on temporary stream we open it always read-write
1620 aArgs[0] <<= pImpl->xStream;
1621 aArgs[1] <<= embed::ElementModes::READWRITE;
1622 pImpl->bStorageBasedOnInStream = true;
1623 if (pImpl->m_bDisableFileSync)
1625 // Forward NoFileSync to the storage factory.
1626 aArgs.realloc(3);
1627 uno::Sequence<beans::PropertyValue> aProperties(
1628 comphelper::InitPropertySequence({ { "NoFileSync", uno::makeAny(true) } }));
1629 aArgs[2] <<= aProperties;
1632 else if ( pImpl->xInputStream.is() )
1634 // since the storage is based on temporary stream we open it always read-write
1635 aArgs[0] <<= pImpl->xInputStream;
1636 aArgs[1] <<= embed::ElementModes::READ;
1637 pImpl->bStorageBasedOnInStream = true;
1639 else
1641 CloseStreams_Impl();
1642 aArgs[0] <<= pImpl->m_aName;
1643 aArgs[1] <<= embed::ElementModes::READ;
1644 pImpl->bStorageBasedOnInStream = false;
1649 pImpl->xStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ),
1650 uno::UNO_QUERY );
1652 catch( const uno::Exception& )
1654 // impossibility to create the storage is no error
1657 if( ( pImpl->nLastStorageError = GetError() ) != ERRCODE_NONE )
1659 pImpl->xStorage = nullptr;
1660 if ( pImpl->m_pInStream )
1661 pImpl->m_pInStream->Seek(0);
1662 return uno::Reference< embed::XStorage >();
1665 pImpl->m_bTriedStorage = true;
1667 // TODO/LATER: Get versionlist on demand
1668 if ( pImpl->xStorage.is() )
1670 SetEncryptionDataToStorage_Impl();
1671 GetVersionList();
1674 const SfxInt16Item* pVersion = SfxItemSet::GetItem<SfxInt16Item>(pImpl->m_pSet.get(), SID_VERSION, false);
1676 bool bResetStorage = false;
1677 if ( pVersion && pVersion->GetValue() )
1679 // Read all available versions
1680 if ( pImpl->aVersions.hasElements() )
1682 // Search for the version fits the comment
1683 // The versions are numbered starting with 1, versions with
1684 // negative versions numbers are counted backwards from the
1685 // current version
1686 short nVersion = pVersion->GetValue();
1687 if ( nVersion<0 )
1688 nVersion = static_cast<short>(pImpl->aVersions.getLength()) + nVersion;
1689 else // nVersion > 0; pVersion->GetValue() != 0 was the condition to this block
1690 nVersion--;
1692 util::RevisionTag& rTag = pImpl->aVersions[nVersion];
1694 // Open SubStorage for all versions
1695 uno::Reference < embed::XStorage > xSub = pImpl->xStorage->openStorageElement( "Versions",
1696 embed::ElementModes::READ );
1698 DBG_ASSERT( xSub.is(), "Version list, but no Versions!" );
1700 // There the version is stored as packed Stream
1701 uno::Reference < io::XStream > xStr = xSub->openStreamElement( rTag.Identifier, embed::ElementModes::READ );
1702 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( xStr ));
1703 if ( pStream && pStream->GetError() == ERRCODE_NONE )
1705 // Unpack Stream in TempDir
1706 ::utl::TempFile aTempFile;
1707 const OUString& aTmpName = aTempFile.GetURL();
1708 SvFileStream aTmpStream( aTmpName, SFX_STREAM_READWRITE );
1710 pStream->ReadStream( aTmpStream );
1711 pStream.reset();
1712 aTmpStream.Close();
1714 // Open data as Storage
1715 pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
1716 pImpl->xStorage = comphelper::OStorageHelper::GetStorageFromURL( aTmpName, embed::ElementModes::READ );
1717 pImpl->bStorageBasedOnInStream = false;
1718 OUString aTemp;
1719 osl::FileBase::getSystemPathFromFileURL( aTmpName, aTemp );
1720 SetPhysicalName_Impl( aTemp );
1722 pImpl->bIsTemp = true;
1723 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1724 // TODO/MBA
1725 pImpl->aVersions.realloc(0);
1727 else
1728 bResetStorage = true;
1731 else
1732 bResetStorage = true;
1735 if ( bResetStorage )
1737 pImpl->xStorage.clear();
1738 if ( pImpl->m_pInStream )
1739 pImpl->m_pInStream->Seek( 0 );
1742 pImpl->bIsStorage = pImpl->xStorage.is();
1743 return pImpl->xStorage;
1747 uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly )
1749 if ( !GetError() && !pImpl->m_xZipStorage.is() )
1751 GetMedium_Impl();
1755 // we can not sign document if there is no stream
1756 // should it be possible at all?
1757 if ( !bReadOnly && pImpl->xStream.is() )
1759 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
1761 else if ( pImpl->xInputStream.is() )
1763 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream );
1766 catch( const uno::Exception& )
1768 SAL_WARN( "sfx.doc", "No possibility to get readonly version of storage from medium!" );
1771 if ( GetError() ) // do not remove warnings
1772 ResetError();
1775 return pImpl->m_xZipStorage;
1779 void SfxMedium::CloseZipStorage_Impl()
1781 if ( pImpl->m_xZipStorage.is() )
1783 try {
1784 pImpl->m_xZipStorage->dispose();
1785 } catch( const uno::Exception& )
1788 pImpl->m_xZipStorage.clear();
1792 void SfxMedium::CloseStorage()
1794 if ( pImpl->xStorage.is() )
1796 uno::Reference < lang::XComponent > xComp = pImpl->xStorage;
1797 // in the salvage mode the medium does not own the storage
1798 if ( pImpl->bDisposeStorage && !pImpl->m_bSalvageMode )
1800 try {
1801 xComp->dispose();
1802 } catch( const uno::Exception& )
1804 SAL_WARN( "sfx.doc", "Medium's storage is already disposed!" );
1808 pImpl->xStorage.clear();
1809 pImpl->bStorageBasedOnInStream = false;
1812 pImpl->m_bTriedStorage = false;
1813 pImpl->bIsStorage = false;
1816 void SfxMedium::CanDisposeStorage_Impl( bool bDisposeStorage )
1818 pImpl->bDisposeStorage = bDisposeStorage;
1821 bool SfxMedium::WillDisposeStorageOnClose_Impl()
1823 return pImpl->bDisposeStorage;
1826 StreamMode SfxMedium::GetOpenMode() const
1828 return pImpl->m_nStorOpenMode;
1831 void SfxMedium::SetOpenMode( StreamMode nStorOpen,
1832 bool bDontClose )
1834 if ( pImpl->m_nStorOpenMode != nStorOpen )
1836 pImpl->m_nStorOpenMode = nStorOpen;
1838 if( !bDontClose )
1840 if ( pImpl->xStorage.is() )
1841 CloseStorage();
1843 CloseStreams_Impl();
1849 bool SfxMedium::UseBackupToRestore_Impl( ::ucbhelper::Content& aOriginalContent,
1850 const Reference< css::ucb::XCommandEnvironment >& xComEnv )
1854 ::ucbhelper::Content aTransactCont( pImpl->m_aBackupURL, xComEnv, comphelper::getProcessComponentContext() );
1856 Reference< XInputStream > aOrigInput = aTransactCont.openStream();
1857 aOriginalContent.writeStream( aOrigInput, true );
1858 return true;
1860 catch( const Exception& )
1862 // in case of failure here the backup file should not be removed
1863 // TODO/LATER: a message should be used to let user know about the backup
1864 pImpl->m_bRemoveBackup = false;
1865 // TODO/LATER: needs a specific error code
1866 pImpl->m_eError = ERRCODE_IO_GENERAL;
1869 return false;
1873 bool SfxMedium::StorageCommit_Impl()
1875 bool bResult = false;
1876 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1877 ::ucbhelper::Content aOriginalContent;
1879 if ( pImpl->xStorage.is() )
1881 if ( !GetError() )
1883 uno::Reference < embed::XTransactedObject > xTrans( pImpl->xStorage, uno::UNO_QUERY );
1884 if ( xTrans.is() )
1888 xTrans->commit();
1889 CloseZipStorage_Impl();
1890 bResult = true;
1892 catch ( const embed::UseBackupException& aBackupExc )
1894 // since the temporary file is created always now, the scenario is close to be impossible
1895 if ( !pImpl->pTempFile )
1897 OSL_ENSURE( !pImpl->m_aBackupURL.isEmpty(), "No backup on storage commit!" );
1898 if ( !pImpl->m_aBackupURL.isEmpty()
1899 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1900 xDummyEnv, comphelper::getProcessComponentContext(),
1901 aOriginalContent ) )
1903 // use backup to restore the file
1904 // the storage has already disconnected from original location
1905 CloseAndReleaseStreams_Impl();
1906 if ( !UseBackupToRestore_Impl( aOriginalContent, xDummyEnv ) )
1908 // connect the medium to the temporary file of the storage
1909 pImpl->aContent = ::ucbhelper::Content();
1910 pImpl->m_aName = aBackupExc.TemporaryFileURL;
1911 OSL_ENSURE( !pImpl->m_aName.isEmpty(), "The exception _must_ contain the temporary URL!" );
1916 if (!GetError())
1917 SetError(ERRCODE_IO_GENERAL);
1919 catch ( const uno::Exception& )
1921 //TODO/LATER: improve error handling
1922 SetError(ERRCODE_IO_GENERAL);
1928 return bResult;
1932 void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject& aSource,
1933 const INetURLObject& aDest,
1934 const Reference< css::ucb::XCommandEnvironment >& xComEnv )
1936 bool bResult = false;
1937 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1938 ::ucbhelper::Content aOriginalContent;
1942 aOriginalContent = ::ucbhelper::Content( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
1944 catch ( const css::ucb::CommandAbortedException& )
1946 pImpl->m_eError = ERRCODE_ABORT;
1948 catch ( const css::ucb::CommandFailedException& )
1950 pImpl->m_eError = ERRCODE_ABORT;
1952 catch (const css::ucb::ContentCreationException& ex)
1954 pImpl->m_eError = ERRCODE_IO_GENERAL;
1955 if (
1956 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) ||
1957 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
1960 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
1963 catch (const css::uno::Exception&)
1965 pImpl->m_eError = ERRCODE_IO_GENERAL;
1968 if( pImpl->m_eError && !pImpl->m_eError.IsWarning() )
1969 return;
1971 if ( pImpl->xStorage.is() )
1972 CloseStorage();
1974 CloseStreams_Impl();
1976 ::ucbhelper::Content aTempCont;
1977 if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aTempCont ) )
1979 bool bTransactStarted = false;
1980 const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
1981 bool bOverWrite = !pOverWrite || pOverWrite->GetValue();
1985 OUString aSourceMainURL = aSource.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1986 OUString aDestMainURL = aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1988 sal_uInt64 nAttributes = GetDefaultFileAttributes(aDestMainURL);
1989 if (IsFileMovable(aDest)
1990 && osl::File::replace(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None)
1992 if (nAttributes)
1993 // Adjust attributes, source might be created with
1994 // the osl_File_OpenFlag_Private flag.
1995 osl::File::setAttributes(aDestMainURL, nAttributes);
1996 bResult = true;
1998 else
2000 if (bOverWrite && ::utl::UCBContentHelper::IsDocument(aDestMainURL))
2002 if( pImpl->m_aBackupURL.isEmpty() )
2003 DoInternalBackup_Impl( aOriginalContent );
2005 if( !pImpl->m_aBackupURL.isEmpty() )
2007 Reference< XInputStream > aTempInput = aTempCont.openStream();
2008 bTransactStarted = true;
2009 aOriginalContent.setPropertyValue( "Size", uno::makeAny( sal_Int64(0) ) );
2010 aOriginalContent.writeStream( aTempInput, bOverWrite );
2011 bResult = true;
2013 else
2015 pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
2018 else
2020 Reference< XInputStream > aTempInput = aTempCont.openStream();
2021 aOriginalContent.writeStream( aTempInput, bOverWrite );
2022 bResult = true;
2026 catch ( const css::ucb::CommandAbortedException& )
2028 pImpl->m_eError = ERRCODE_ABORT;
2030 catch ( const css::ucb::CommandFailedException& )
2032 pImpl->m_eError = ERRCODE_ABORT;
2034 catch ( const css::ucb::InteractiveIOException& r )
2036 if ( r.Code == IOErrorCode_ACCESS_DENIED )
2037 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
2038 else if ( r.Code == IOErrorCode_NOT_EXISTING )
2039 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2040 else if ( r.Code == IOErrorCode_CANT_READ )
2041 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2042 else
2043 pImpl->m_eError = ERRCODE_IO_GENERAL;
2045 catch ( const css::uno::Exception& )
2047 pImpl->m_eError = ERRCODE_IO_GENERAL;
2050 if ( bResult )
2052 if ( pImpl->pTempFile )
2054 pImpl->pTempFile->EnableKillingFile();
2055 pImpl->pTempFile.reset();
2058 else if ( bTransactStarted )
2060 UseBackupToRestore_Impl( aOriginalContent, xDummyEnv );
2063 else
2064 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2068 bool SfxMedium::TryDirectTransfer( const OUString& aURL, SfxItemSet const & aTargetSet )
2070 if ( GetError() )
2071 return false;
2073 // if the document had no password it should be stored without password
2074 // if the document had password it should be stored with the same password
2075 // otherwise the stream copying can not be done
2076 const SfxStringItem* pNewPassItem = aTargetSet.GetItem<SfxStringItem>(SID_PASSWORD, false);
2077 const SfxStringItem* pOldPassItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_PASSWORD, false);
2078 if ( ( !pNewPassItem && !pOldPassItem )
2079 || ( pNewPassItem && pOldPassItem && pNewPassItem->GetValue() == pOldPassItem->GetValue() ) )
2081 // the filter must be the same
2082 const SfxStringItem* pNewFilterItem = aTargetSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
2083 const SfxStringItem* pOldFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_FILTER_NAME, false);
2084 if ( pNewFilterItem && pOldFilterItem && pNewFilterItem->GetValue() == pOldFilterItem->GetValue() )
2086 // get the input stream and copy it
2087 // in case of success return true
2088 uno::Reference< io::XInputStream > xInStream = GetInputStream();
2090 ResetError();
2091 if ( xInStream.is() )
2095 uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
2096 sal_Int64 nPos = 0;
2097 if ( xSeek.is() )
2099 nPos = xSeek->getPosition();
2100 xSeek->seek( 0 );
2103 uno::Reference < css::ucb::XCommandEnvironment > xEnv;
2104 ::ucbhelper::Content aTargetContent( aURL, xEnv, comphelper::getProcessComponentContext() );
2106 InsertCommandArgument aInsertArg;
2107 aInsertArg.Data = xInStream;
2108 const SfxBoolItem* pOverWrite = aTargetSet.GetItem<SfxBoolItem>(SID_OVERWRITE, false);
2109 if ( pOverWrite && !pOverWrite->GetValue() ) // argument says: never overwrite
2110 aInsertArg.ReplaceExisting = false;
2111 else
2112 aInsertArg.ReplaceExisting = true; // default is overwrite existing files
2114 Any aCmdArg;
2115 aCmdArg <<= aInsertArg;
2116 aTargetContent.executeCommand( "insert",
2117 aCmdArg );
2119 if ( xSeek.is() )
2120 xSeek->seek( nPos );
2122 return true;
2124 catch( const uno::Exception& )
2130 return false;
2134 void SfxMedium::Transfer_Impl()
2136 // The transfer is required only in two cases: either if there is a temporary file or if there is a salvage item
2137 OUString aNameURL;
2138 if ( pImpl->pTempFile )
2139 aNameURL = pImpl->pTempFile->GetURL();
2140 else if ( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
2142 // makes sense only in case logic name is set
2143 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aNameURL )
2144 != osl::FileBase::E_None )
2145 SAL_WARN( "sfx.doc", "The medium name is not convertible!" );
2148 if ( aNameURL.isEmpty() || ( pImpl->m_eError && !pImpl->m_eError.IsWarning() ) )
2149 return;
2151 SAL_INFO( "sfx.doc", "SfxMedium::Transfer_Impl, copying to target" );
2153 Reference < css::ucb::XCommandEnvironment > xEnv;
2154 Reference< XOutputStream > rOutStream;
2156 // in case an output stream is provided from outside and the URL is correct
2157 // commit to the stream
2158 if (pImpl->m_aLogicName.startsWith("private:stream"))
2160 // TODO/LATER: support storing to SID_STREAM
2161 const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
2162 if( pOutStreamItem && ( pOutStreamItem->GetValue() >>= rOutStream ) )
2164 if ( pImpl->xStorage.is() )
2165 CloseStorage();
2167 CloseStreams_Impl();
2169 INetURLObject aSource( aNameURL );
2170 ::ucbhelper::Content aTempCont;
2171 if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aTempCont ) )
2175 sal_Int32 nRead;
2176 sal_Int32 nBufferSize = 32767;
2177 Sequence < sal_Int8 > aSequence ( nBufferSize );
2178 Reference< XInputStream > aTempInput = aTempCont.openStream();
2182 nRead = aTempInput->readBytes ( aSequence, nBufferSize );
2183 if ( nRead < nBufferSize )
2185 Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
2186 rOutStream->writeBytes ( aTempBuf );
2188 else
2189 rOutStream->writeBytes ( aSequence );
2191 while ( nRead == nBufferSize );
2193 // remove temporary file
2194 if ( pImpl->pTempFile )
2196 pImpl->pTempFile->EnableKillingFile();
2197 pImpl->pTempFile.reset();
2200 catch( const Exception& )
2204 else
2206 SAL_WARN( "sfx.doc", "Illegal Output stream parameter!" );
2207 SetError(ERRCODE_IO_GENERAL);
2210 // free the reference
2211 if ( pImpl->m_pSet )
2212 pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
2214 return;
2217 GetContent();
2218 if ( !pImpl->aContent.get().is() )
2220 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2221 return;
2224 INetURLObject aDest( GetURLObject() );
2226 // source is the temp file written so far
2227 INetURLObject aSource( aNameURL );
2229 // a special case, an interaction handler should be used for
2230 // authentication in case it is available
2231 Reference< css::ucb::XCommandEnvironment > xComEnv;
2232 Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
2233 if (xInteractionHandler.is())
2234 xComEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler,
2235 Reference< css::ucb::XProgressHandler >() );
2237 OUString aDestURL( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2239 if ( comphelper::isFileUrl( aDestURL ) || !aDest.removeSegment() )
2241 TransactedTransferForFS_Impl( aSource, aDest, xComEnv );
2243 if (!pImpl->m_bDisableFileSync)
2245 // Hideous - no clean way to do this, so we re-open the file just to fsync it
2246 osl::File aFile( aDestURL );
2247 if ( aFile.open( osl_File_OpenFlag_Write ) == osl::FileBase::E_None )
2249 aFile.sync();
2250 SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL << "'" );
2251 aFile.close();
2255 else
2257 // create content for the parent folder and call transfer on that content with the source content
2258 // and the destination file name as parameters
2259 ::ucbhelper::Content aSourceContent;
2260 ::ucbhelper::Content aTransferContent;
2262 ::ucbhelper::Content aDestContent;
2263 (void)::ucbhelper::Content::create( aDestURL, xComEnv, comphelper::getProcessComponentContext(), aDestContent );
2264 // For checkin, we need the object URL, not the parent folder:
2265 if ( !IsInCheckIn( ) )
2267 // Get the parent URL from the XChild if possible: why would the URL necessarily have
2268 // a hierarchical path? It's not always the case for CMIS.
2269 Reference< css::container::XChild> xChild( aDestContent.get(), uno::UNO_QUERY );
2270 OUString sParentUrl;
2271 if ( xChild.is( ) )
2273 Reference< css::ucb::XContent > xParent( xChild->getParent( ), uno::UNO_QUERY );
2274 if ( xParent.is( ) )
2276 sParentUrl = xParent->getIdentifier( )->getContentIdentifier();
2280 if ( sParentUrl.isEmpty() )
2281 aDestURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2282 // adjust to above aDest.removeSegment()
2283 else
2284 aDestURL = sParentUrl;
2287 // LongName wasn't defined anywhere, only used here... get the Title instead
2288 // as it's less probably empty
2289 OUString aFileName;
2290 Any aAny = aDestContent.getPropertyValue("Title");
2291 aAny >>= aFileName;
2292 aAny = aDestContent.getPropertyValue( "ObjectId" );
2293 OUString sObjectId;
2294 aAny >>= sObjectId;
2295 if ( aFileName.isEmpty() )
2296 aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2300 aTransferContent = ::ucbhelper::Content( aDestURL, xComEnv, comphelper::getProcessComponentContext() );
2302 catch (const css::ucb::ContentCreationException& ex)
2304 pImpl->m_eError = ERRCODE_IO_GENERAL;
2305 if (
2306 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) ||
2307 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
2310 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
2313 catch (const css::uno::Exception&)
2315 pImpl->m_eError = ERRCODE_IO_GENERAL;
2318 if ( !pImpl->m_eError || pImpl->m_eError.IsWarning() )
2320 // free resources, otherwise the transfer may fail
2321 if ( pImpl->xStorage.is() )
2322 CloseStorage();
2324 CloseStreams_Impl();
2326 (void)::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent );
2328 // check for external parameters that may customize the handling of NameClash situations
2329 const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
2330 sal_Int32 nNameClash;
2331 if ( pOverWrite && !pOverWrite->GetValue() )
2332 // argument says: never overwrite
2333 nNameClash = NameClash::ERROR;
2334 else
2335 // default is overwrite existing files
2336 nNameClash = NameClash::OVERWRITE;
2340 OUString aMimeType = pImpl->getFilterMimeType();
2341 ::ucbhelper::InsertOperation eOperation = ::ucbhelper::InsertOperation::Copy;
2342 bool bMajor = false;
2343 OUString sComment;
2344 if ( IsInCheckIn( ) )
2346 eOperation = ::ucbhelper::InsertOperation::Checkin;
2347 const SfxBoolItem* pMajor = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOCINFO_MAJOR, false);
2348 bMajor = pMajor && pMajor->GetValue( );
2349 const SfxStringItem* pComments = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_DOCINFO_COMMENTS, false);
2350 if ( pComments )
2351 sComment = pComments->GetValue( );
2353 OUString sResultURL;
2354 aTransferContent.transferContent(
2355 aSourceContent, eOperation,
2356 aFileName, nNameClash, aMimeType, bMajor, sComment,
2357 &sResultURL, sObjectId );
2359 if ( !sResultURL.isEmpty( ) ) // Likely to happen only for checkin
2360 SwitchDocumentToFile( sResultURL );
2363 if ( GetURLObject().isAnyKnownWebDAVScheme() &&
2364 eOperation == ::ucbhelper::InsertOperation::Copy )
2366 // tdf#95272 try to re-issue a lock command when a new file is created.
2367 // This may be needed because some WebDAV servers fail to implement the
2368 // 'LOCK on unallocated reference', see issue comment:
2369 // <https://bugs.documentfoundation.org/show_bug.cgi?id=95792#c8>
2370 // and specification at:
2371 // <http://tools.ietf.org/html/rfc4918#section-7.3>
2372 // If the WebDAV resource is already locked by this LO instance, nothing will
2373 // happen, e.g. the LOCK method will not be sent to the server.
2374 ::ucbhelper::Content aLockContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
2375 aLockContent.lock();
2378 catch ( css::uno::Exception & )
2380 TOOLS_WARN_EXCEPTION( "sfx.doc", "LOCK not working while re-issuing it" );
2383 catch ( const css::ucb::CommandAbortedException& )
2385 pImpl->m_eError = ERRCODE_ABORT;
2387 catch ( const css::ucb::CommandFailedException& )
2389 pImpl->m_eError = ERRCODE_ABORT;
2391 catch ( const css::ucb::InteractiveIOException& r )
2393 if ( r.Code == IOErrorCode_ACCESS_DENIED )
2394 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
2395 else if ( r.Code == IOErrorCode_NOT_EXISTING )
2396 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2397 else if ( r.Code == IOErrorCode_CANT_READ )
2398 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2399 else
2400 pImpl->m_eError = ERRCODE_IO_GENERAL;
2402 catch ( const css::uno::Exception& )
2404 pImpl->m_eError = ERRCODE_IO_GENERAL;
2407 // do not switch from temporary file in case of nonfile protocol
2411 if ( ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) && !pImpl->pTempFile )
2413 // without a TempFile the physical and logical name should be the same after successful transfer
2414 if (osl::FileBase::getSystemPathFromFileURL(
2415 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName )
2416 != osl::FileBase::E_None)
2418 pImpl->m_aName.clear();
2420 pImpl->m_bSalvageMode = false;
2425 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent,
2426 const OUString& aPrefix,
2427 const OUString& aExtension,
2428 const OUString& aDestDir )
2430 if ( !pImpl->m_aBackupURL.isEmpty() )
2431 return; // the backup was done already
2433 ::utl::TempFile aTransactTemp( aPrefix, true, &aExtension, &aDestDir );
2435 INetURLObject aBackObj( aTransactTemp.GetURL() );
2436 OUString aBackupName = aBackObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2438 Reference < css::ucb::XCommandEnvironment > xDummyEnv;
2439 ::ucbhelper::Content aBackupCont;
2440 if( ::ucbhelper::Content::create( aDestDir, xDummyEnv, comphelper::getProcessComponentContext(), aBackupCont ) )
2444 OUString sMimeType = pImpl->getFilterMimeType();
2445 aBackupCont.transferContent( aOriginalContent,
2446 ::ucbhelper::InsertOperation::Copy,
2447 aBackupName,
2448 NameClash::OVERWRITE,
2449 sMimeType );
2450 pImpl->m_aBackupURL = aBackObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2451 pImpl->m_bRemoveBackup = true;
2453 catch( const Exception& )
2457 if ( pImpl->m_aBackupURL.isEmpty() )
2458 aTransactTemp.EnableKillingFile();
2462 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent )
2464 if ( !pImpl->m_aBackupURL.isEmpty() )
2465 return; // the backup was done already
2467 OUString aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT,
2468 true,
2469 INetURLObject::DecodeMechanism::NONE );
2471 sal_Int32 nPrefixLen = aFileName.lastIndexOf( '.' );
2472 OUString aPrefix = ( nPrefixLen == -1 ) ? aFileName : aFileName.copy( 0, nPrefixLen );
2473 OUString aExtension = ( nPrefixLen == -1 ) ? OUString() : aFileName.copy( nPrefixLen );
2474 OUString aBakDir = SvtPathOptions().GetBackupPath();
2476 // create content for the parent folder ( = backup folder )
2477 ::ucbhelper::Content aContent;
2478 Reference < css::ucb::XCommandEnvironment > xEnv;
2479 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
2480 DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aBakDir );
2482 if ( !pImpl->m_aBackupURL.isEmpty() )
2483 return;
2485 // the copying to the backup catalog failed ( for example because
2486 // of using an encrypted partition as target catalog )
2487 // since the user did not specify to make backup explicitly
2488 // office should try to make backup in another place,
2489 // target catalog does not look bad for this case ( and looks
2490 // to be the only way for encrypted partitions )
2492 INetURLObject aDest = GetURLObject();
2493 if ( aDest.removeSegment() )
2494 DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2498 void SfxMedium::DoBackup_Impl()
2500 // source file name is the logical name of this medium
2501 INetURLObject aSource( GetURLObject() );
2503 // there is nothing to backup in case source file does not exist
2504 if ( !::utl::UCBContentHelper::IsDocument( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
2505 return;
2507 bool bSuccess = false;
2509 // get path for backups
2510 OUString aBakDir = SvtPathOptions().GetBackupPath();
2511 if( !aBakDir.isEmpty() )
2513 // create content for the parent folder ( = backup folder )
2514 ::ucbhelper::Content aContent;
2515 Reference < css::ucb::XCommandEnvironment > xEnv;
2516 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
2518 // save as ".bak" file
2519 INetURLObject aDest( aBakDir );
2520 aDest.insertName( aSource.getName() );
2521 aDest.setExtension( "bak" );
2522 OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2524 // create a content for the source file
2525 ::ucbhelper::Content aSourceContent;
2526 if ( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) )
2530 // do the transfer ( copy source file to backup dir )
2531 OUString sMimeType = pImpl->getFilterMimeType();
2532 aContent.transferContent( aSourceContent,
2533 ::ucbhelper::InsertOperation::Copy,
2534 aFileName,
2535 NameClash::OVERWRITE,
2536 sMimeType );
2537 pImpl->m_aBackupURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2538 pImpl->m_bRemoveBackup = false;
2539 bSuccess = true;
2541 catch ( const css::uno::Exception& )
2548 if ( !bSuccess )
2550 pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
2555 void SfxMedium::ClearBackup_Impl()
2557 if( pImpl->m_bRemoveBackup )
2559 // currently a document is always stored in a new medium,
2560 // thus if a backup can not be removed the backup URL should not be cleaned
2561 if ( !pImpl->m_aBackupURL.isEmpty() )
2563 if ( ::utl::UCBContentHelper::Kill( pImpl->m_aBackupURL ) )
2565 pImpl->m_bRemoveBackup = false;
2566 pImpl->m_aBackupURL.clear();
2568 else
2571 SAL_WARN( "sfx.doc", "Couldn't remove backup file!");
2575 else
2576 pImpl->m_aBackupURL.clear();
2580 void SfxMedium::GetLockingStream_Impl()
2582 if ( GetURLObject().GetProtocol() != INetProtocol::File
2583 || pImpl->m_xLockingStream.is() )
2584 return;
2586 const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
2587 if ( pWriteStreamItem )
2588 pWriteStreamItem->GetValue() >>= pImpl->m_xLockingStream;
2590 if ( pImpl->m_xLockingStream.is() )
2591 return;
2593 // open the original document
2594 uno::Sequence< beans::PropertyValue > xProps;
2595 TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
2596 utl::MediaDescriptor aMedium( xProps );
2598 aMedium.addInputStreamOwnLock();
2600 uno::Reference< io::XInputStream > xInputStream;
2601 aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->m_xLockingStream;
2602 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= xInputStream;
2604 if ( !pImpl->pTempFile && pImpl->m_aName.isEmpty() )
2606 // the medium is still based on the original file, it makes sense to initialize the streams
2607 if ( pImpl->m_xLockingStream.is() )
2608 pImpl->xStream = pImpl->m_xLockingStream;
2610 if ( xInputStream.is() )
2611 pImpl->xInputStream = xInputStream;
2613 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2614 pImpl->xInputStream = pImpl->xStream->getInputStream();
2619 void SfxMedium::GetMedium_Impl()
2621 if ( pImpl->m_pInStream
2622 && (!pImpl->bIsTemp || pImpl->xInputStream.is() || pImpl->m_xInputStreamToLoadFrom.is() || pImpl->xStream.is() || pImpl->m_xLockingStream.is() ) )
2623 return;
2625 pImpl->bDownloadDone = false;
2626 Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
2628 //TODO/MBA: need support for SID_STREAM
2629 const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
2630 const SfxUnoAnyItem* pInStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INPUTSTREAM, false);
2631 if ( pWriteStreamItem )
2633 pWriteStreamItem->GetValue() >>= pImpl->xStream;
2635 if ( pInStreamItem )
2636 pInStreamItem->GetValue() >>= pImpl->xInputStream;
2638 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2639 pImpl->xInputStream = pImpl->xStream->getInputStream();
2641 else if ( pInStreamItem )
2643 pInStreamItem->GetValue() >>= pImpl->xInputStream;
2645 else
2647 uno::Sequence < beans::PropertyValue > xProps;
2648 OUString aFileName;
2649 if (!pImpl->m_aName.isEmpty())
2651 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aFileName )
2652 != osl::FileBase::E_None )
2654 SAL_WARN( "sfx.doc", "Physical name not convertible!");
2657 else
2658 aFileName = GetName();
2660 // in case the temporary file exists the streams should be initialized from it,
2661 // but the original MediaDescriptor should not be changed
2662 bool bFromTempFile = ( pImpl->pTempFile != nullptr );
2664 if ( !bFromTempFile )
2666 GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, aFileName ) );
2667 if( !(pImpl->m_nStorOpenMode & StreamMode::WRITE) )
2668 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
2669 if (xInteractionHandler.is())
2670 GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, makeAny(xInteractionHandler) ) );
2673 if ( pImpl->m_xInputStreamToLoadFrom.is() )
2675 pImpl->xInputStream = pImpl->m_xInputStreamToLoadFrom;
2676 if (pImpl->m_bInputStreamIsReadOnly)
2677 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
2679 else
2681 TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
2682 utl::MediaDescriptor aMedium( xProps );
2684 if ( pImpl->m_xLockingStream.is() && !bFromTempFile )
2686 // the medium is not based on the temporary file, so the original stream can be used
2687 pImpl->xStream = pImpl->m_xLockingStream;
2689 else
2691 if ( bFromTempFile )
2693 aMedium[utl::MediaDescriptor::PROP_URL()] <<= aFileName;
2694 aMedium.erase( utl::MediaDescriptor::PROP_READONLY() );
2695 aMedium.addInputStream();
2697 else if ( GetURLObject().GetProtocol() == INetProtocol::File )
2699 // use the special locking approach only for file URLs
2700 aMedium.addInputStreamOwnLock();
2702 else
2704 // add a check for protocol, if it's http or https or provide webdav then add
2705 // the interaction handler to be used by the authentication dialog
2706 if ( GetURLObject().isAnyKnownWebDAVScheme() )
2708 aMedium[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER()] <<= GetInteractionHandler( true );
2710 aMedium.addInputStream();
2712 // the ReadOnly property set in aMedium is ignored
2713 // the check is done in LockOrigFileOnDemand() for file and non-file URLs
2715 //TODO/MBA: what happens if property is not there?!
2716 aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->xStream;
2717 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= pImpl->xInputStream;
2720 GetContent();
2721 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2722 pImpl->xInputStream = pImpl->xStream->getInputStream();
2725 if ( !bFromTempFile )
2727 //TODO/MBA: need support for SID_STREAM
2728 if ( pImpl->xStream.is() )
2729 GetItemSet()->Put( SfxUnoAnyItem( SID_STREAM, makeAny( pImpl->xStream ) ) );
2731 GetItemSet()->Put( SfxUnoAnyItem( SID_INPUTSTREAM, makeAny( pImpl->xInputStream ) ) );
2735 //TODO/MBA: ErrorHandling - how to transport error from MediaDescriptor
2736 if ( !GetError() && !pImpl->xStream.is() && !pImpl->xInputStream.is() )
2737 SetError(ERRCODE_IO_ACCESSDENIED);
2739 if ( !GetError() && !pImpl->m_pInStream )
2741 if ( pImpl->xStream.is() )
2742 pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xStream );
2743 else if ( pImpl->xInputStream.is() )
2744 pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xInputStream );
2747 pImpl->bDownloadDone = true;
2748 pImpl->aDoneLink.ClearPendingCall();
2749 ErrCode nError = GetError();
2750 pImpl->aDoneLink.Call( reinterpret_cast<void*>(sal_uInt32(nError)) );
2753 bool SfxMedium::IsRemote() const
2755 return pImpl->m_bRemote;
2758 void SfxMedium::SetUpdatePickList(bool bVal)
2760 pImpl->bUpdatePickList = bVal;
2763 bool SfxMedium::IsUpdatePickList() const
2765 return pImpl->bUpdatePickList;
2768 void SfxMedium::SetLongName(const OUString &rName)
2770 pImpl->m_aLongName = rName;
2773 const OUString& SfxMedium::GetLongName() const
2775 return pImpl->m_aLongName;
2778 void SfxMedium::SetDoneLink( const Link<void*,void>& rLink )
2780 pImpl->aDoneLink = rLink;
2783 void SfxMedium::Download( const Link<void*,void>& aLink )
2785 SetDoneLink( aLink );
2786 GetInStream();
2787 if ( pImpl->m_pInStream && !aLink.IsSet() )
2789 while( !pImpl->bDownloadDone )
2790 Application::Yield();
2795 void SfxMedium::Init_Impl()
2796 /* [Description]
2797 Includes a valid:: sun:: com:: star:: util:: URL (If a file name was
2798 previously in there) in the logical name and if available sets the
2799 physical name as the file name.
2803 Reference< XOutputStream > rOutStream;
2805 // TODO/LATER: handle lifetime of storages
2806 pImpl->bDisposeStorage = false;
2808 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
2809 if ( pSalvageItem && pSalvageItem->GetValue().isEmpty() )
2811 pSalvageItem = nullptr;
2812 pImpl->m_pSet->ClearItem( SID_DOC_SALVAGE );
2815 if (!pImpl->m_aLogicName.isEmpty())
2817 INetURLObject aUrl( pImpl->m_aLogicName );
2818 INetProtocol eProt = aUrl.GetProtocol();
2819 if ( eProt == INetProtocol::NotValid )
2821 SAL_WARN( "sfx.doc", "URL <" << pImpl->m_aLogicName << "> with unknown protocol" );
2823 else
2825 if ( aUrl.HasMark() )
2827 pImpl->m_aLogicName = aUrl.GetURLNoMark( INetURLObject::DecodeMechanism::NONE );
2828 GetItemSet()->Put( SfxStringItem( SID_JUMPMARK, aUrl.GetMark() ) );
2831 // try to convert the URL into a physical name - but never change a physical name
2832 // physical name may be set if the logical name is changed after construction
2833 if ( pImpl->m_aName.isEmpty() )
2834 osl::FileBase::getSystemPathFromFileURL( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName );
2835 else
2837 DBG_ASSERT( pSalvageItem, "Suspicious change of logical name!" );
2842 if ( pSalvageItem && !pSalvageItem->GetValue().isEmpty() )
2844 pImpl->m_aLogicName = pSalvageItem->GetValue();
2845 pImpl->m_pURLObj.reset();
2846 pImpl->m_bSalvageMode = true;
2849 // in case output stream is by mistake here
2850 // clear the reference
2851 const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
2852 if( pOutStreamItem
2853 && ( !( pOutStreamItem->GetValue() >>= rOutStream )
2854 || !pImpl->m_aLogicName.startsWith("private:stream")) )
2856 pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
2857 SAL_WARN( "sfx.doc", "Unexpected Output stream parameter!" );
2860 if (!pImpl->m_aLogicName.isEmpty())
2862 // if the logic name is set it should be set in MediaDescriptor as well
2863 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
2864 if ( !pFileNameItem )
2866 // let the ItemSet be created if necessary
2867 GetItemSet()->Put(
2868 SfxStringItem(
2869 SID_FILE_NAME, INetURLObject( pImpl->m_aLogicName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
2873 SetIsRemote_Impl();
2875 osl::DirectoryItem item;
2876 if (osl::DirectoryItem::get(GetName(), item) == osl::FileBase::E_None) {
2877 osl::FileStatus stat(osl_FileStatus_Mask_Attributes);
2878 if (item.getFileStatus(stat) == osl::FileBase::E_None
2879 && stat.isValid(osl_FileStatus_Mask_Attributes))
2881 if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0) {
2882 pImpl->m_bOriginallyReadOnly = true;
2889 SfxMedium::SfxMedium() : pImpl(new SfxMedium_Impl)
2891 Init_Impl();
2895 void SfxMedium::UseInteractionHandler( bool bUse )
2897 pImpl->bAllowDefaultIntHdl = bUse;
2901 css::uno::Reference< css::task::XInteractionHandler >
2902 SfxMedium::GetInteractionHandler( bool bGetAlways )
2904 // if interaction isn't allowed explicitly ... return empty reference!
2905 if ( !bGetAlways && !pImpl->bUseInteractionHandler )
2906 return css::uno::Reference< css::task::XInteractionHandler >();
2908 // search a possible existing handler inside cached item set
2909 if ( pImpl->m_pSet )
2911 css::uno::Reference< css::task::XInteractionHandler > xHandler;
2912 const SfxUnoAnyItem* pHandler = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INTERACTIONHANDLER, false);
2913 if ( pHandler && (pHandler->GetValue() >>= xHandler) && xHandler.is() )
2914 return xHandler;
2917 // if default interaction isn't allowed explicitly ... return empty reference!
2918 if ( !bGetAlways && !pImpl->bAllowDefaultIntHdl )
2919 return css::uno::Reference< css::task::XInteractionHandler >();
2921 // otherwise return cached default handler ... if it exist.
2922 if ( pImpl->xInteraction.is() )
2923 return pImpl->xInteraction;
2925 // create default handler and cache it!
2926 Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2927 pImpl->xInteraction.set(
2928 task::InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
2929 return pImpl->xInteraction;
2933 void SfxMedium::SetFilter( const std::shared_ptr<const SfxFilter>& pFilter )
2935 pImpl->m_pFilter = pFilter;
2938 const std::shared_ptr<const SfxFilter>& SfxMedium::GetFilter() const
2940 return pImpl->m_pFilter;
2943 sal_uInt32 SfxMedium::CreatePasswordToModifyHash( const OUString& aPasswd, bool bWriter )
2945 sal_uInt32 nHash = 0;
2947 if ( !aPasswd.isEmpty() )
2949 if ( bWriter )
2951 nHash = ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd );
2953 else
2955 rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
2956 nHash = ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd, nEncoding );
2960 return nHash;
2964 void SfxMedium::Close(bool bInDestruction)
2966 if ( pImpl->xStorage.is() )
2968 CloseStorage();
2971 CloseStreams_Impl(bInDestruction);
2973 UnlockFile( false );
2976 void SfxMedium::CloseAndRelease()
2978 if ( pImpl->xStorage.is() )
2980 CloseStorage();
2983 CloseAndReleaseStreams_Impl();
2985 UnlockFile( true );
2988 void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV )
2990 pImpl->m_bDisableUnlockWebDAV = bDisableUnlockWebDAV;
2993 void SfxMedium::DisableFileSync(bool bDisableFileSync)
2995 pImpl->m_bDisableFileSync = bDisableFileSync;
2998 void SfxMedium::UnlockFile( bool bReleaseLockStream )
3000 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
3001 (void) bReleaseLockStream;
3002 #else
3003 // check if webdav
3004 if ( GetURLObject().isAnyKnownWebDAVScheme() )
3006 // do nothing if WebDAV locking if disabled
3007 // (shouldn't happen because we already skipped locking,
3008 // see LockOrigFileOnDemand, but just in case ...)
3009 if (!IsWebDAVLockingUsed())
3010 return;
3012 if ( pImpl->m_bLocked )
3014 // an interaction handler should be used for authentication, if needed
3015 try {
3016 uno::Reference< css::task::XInteractionHandler > xHandler = GetInteractionHandler( true );
3017 uno::Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment( xHandler,
3018 Reference< css::ucb::XProgressHandler >() );
3019 ucbhelper::Content aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext());
3020 pImpl->m_bLocked = false;
3021 //check if WebDAV unlock was explicitly disabled
3022 if ( !pImpl->m_bDisableUnlockWebDAV )
3023 aContentToUnlock.unlock();
3025 catch ( uno::Exception& )
3027 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
3030 return;
3033 if ( pImpl->m_xLockingStream.is() )
3035 if ( bReleaseLockStream )
3039 uno::Reference< io::XInputStream > xInStream = pImpl->m_xLockingStream->getInputStream();
3040 uno::Reference< io::XOutputStream > xOutStream = pImpl->m_xLockingStream->getOutputStream();
3041 if ( xInStream.is() )
3042 xInStream->closeInput();
3043 if ( xOutStream.is() )
3044 xOutStream->closeOutput();
3046 catch( const uno::Exception& )
3050 pImpl->m_xLockingStream.clear();
3053 if ( !pImpl->m_bLocked )
3054 return;
3056 ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
3060 pImpl->m_bLocked = false;
3061 // TODO/LATER: A warning could be shown in case the file is not the own one
3062 aLockFile.RemoveFile();
3064 catch( const io::WrongFormatException& )
3068 // erase the empty or corrupt file
3069 aLockFile.RemoveFileDirectly();
3071 catch( const uno::Exception& )
3074 catch( const uno::Exception& )
3077 if(pImpl->m_bMSOLockFileCreated)
3079 ::svt::MSODocumentLockFile aMSOLockFile( pImpl->m_aLogicName );
3083 pImpl->m_bLocked = false;
3084 // TODO/LATER: A warning could be shown in case the file is not the own one
3085 aMSOLockFile.RemoveFile();
3087 catch( const io::WrongFormatException& )
3091 // erase the empty or corrupt file
3092 aMSOLockFile.RemoveFileDirectly();
3094 catch( const uno::Exception& )
3097 catch( const uno::Exception& )
3099 pImpl->m_bMSOLockFileCreated = false;
3101 #endif
3104 void SfxMedium::CloseAndReleaseStreams_Impl()
3106 CloseZipStorage_Impl();
3108 uno::Reference< io::XInputStream > xInToClose = pImpl->xInputStream;
3109 uno::Reference< io::XOutputStream > xOutToClose;
3110 if ( pImpl->xStream.is() )
3112 xOutToClose = pImpl->xStream->getOutputStream();
3114 // if the locking stream is closed here the related member should be cleaned
3115 if ( pImpl->xStream == pImpl->m_xLockingStream )
3116 pImpl->m_xLockingStream.clear();
3119 // The probably existing SvStream wrappers should be closed first
3120 CloseStreams_Impl();
3122 // in case of salvage mode the storage is based on the streams
3123 if ( pImpl->m_bSalvageMode )
3124 return;
3128 if ( xInToClose.is() )
3129 xInToClose->closeInput();
3130 if ( xOutToClose.is() )
3131 xOutToClose->closeOutput();
3133 catch ( const uno::Exception& )
3139 void SfxMedium::CloseStreams_Impl(bool bInDestruction)
3141 CloseInStream_Impl(bInDestruction);
3142 CloseOutStream_Impl();
3144 if ( pImpl->m_pSet )
3145 pImpl->m_pSet->ClearItem( SID_CONTENT );
3147 pImpl->aContent = ::ucbhelper::Content();
3151 void SfxMedium::SetIsRemote_Impl()
3153 INetURLObject aObj( GetName() );
3154 switch( aObj.GetProtocol() )
3156 case INetProtocol::Ftp:
3157 case INetProtocol::Http:
3158 case INetProtocol::Https:
3159 pImpl->m_bRemote = true;
3160 break;
3161 default:
3162 pImpl->m_bRemote = GetName().startsWith("private:msgid");
3163 break;
3166 // As files that are written to the remote transmission must also be able
3167 // to be read.
3168 if (pImpl->m_bRemote)
3169 pImpl->m_nStorOpenMode |= StreamMode::READ;
3173 void SfxMedium::SetName( const OUString& aNameP, bool bSetOrigURL )
3175 if (pImpl->aOrigURL.isEmpty())
3176 pImpl->aOrigURL = pImpl->m_aLogicName;
3177 if( bSetOrigURL )
3178 pImpl->aOrigURL = aNameP;
3179 pImpl->m_aLogicName = aNameP;
3180 pImpl->m_pURLObj.reset();
3181 pImpl->aContent = ::ucbhelper::Content();
3182 Init_Impl();
3186 const OUString& SfxMedium::GetOrigURL() const
3188 return pImpl->aOrigURL.isEmpty() ? pImpl->m_aLogicName : pImpl->aOrigURL;
3192 void SfxMedium::SetPhysicalName_Impl( const OUString& rNameP )
3194 if ( rNameP != pImpl->m_aName )
3196 pImpl->pTempFile.reset();
3198 if ( !pImpl->m_aName.isEmpty() || !rNameP.isEmpty() )
3199 pImpl->aContent = ::ucbhelper::Content();
3201 pImpl->m_aName = rNameP;
3202 pImpl->m_bTriedStorage = false;
3203 pImpl->bIsStorage = false;
3208 void SfxMedium::ReOpen()
3210 bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
3211 pImpl->bUseInteractionHandler = false;
3212 GetMedium_Impl();
3213 pImpl->bUseInteractionHandler = bUseInteractionHandler;
3217 void SfxMedium::CompleteReOpen()
3219 // do not use temporary file for reopen and in case of success throw the temporary file away
3220 bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
3221 pImpl->bUseInteractionHandler = false;
3223 std::unique_ptr<::utl::TempFile> pTmpFile;
3224 if ( pImpl->pTempFile )
3226 pTmpFile = std::move(pImpl->pTempFile);
3227 pImpl->m_aName.clear();
3230 GetMedium_Impl();
3232 if ( GetError() )
3234 if ( pImpl->pTempFile )
3236 pImpl->pTempFile->EnableKillingFile();
3237 pImpl->pTempFile.reset();
3239 pImpl->pTempFile = std::move( pTmpFile );
3240 if ( pImpl->pTempFile )
3241 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3243 else if (pTmpFile)
3245 pTmpFile->EnableKillingFile();
3246 pTmpFile.reset();
3249 pImpl->bUseInteractionHandler = bUseInteractionHandler;
3252 SfxMedium::SfxMedium(const OUString &rName, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, const std::shared_ptr<SfxItemSet>& pInSet) :
3253 pImpl(new SfxMedium_Impl)
3255 pImpl->m_pSet = pInSet;
3256 pImpl->m_pFilter = std::move(pFilter);
3257 pImpl->m_aLogicName = rName;
3258 pImpl->m_nStorOpenMode = nOpenMode;
3259 Init_Impl();
3262 SfxMedium::SfxMedium(const OUString &rName, const OUString &rReferer, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, const std::shared_ptr<SfxItemSet>& pInSet) :
3263 pImpl(new SfxMedium_Impl)
3265 pImpl->m_pSet = pInSet;
3266 SfxItemSet * s = GetItemSet();
3267 if (s->GetItem(SID_REFERER) == nullptr) {
3268 s->Put(SfxStringItem(SID_REFERER, rReferer));
3270 pImpl->m_pFilter = std::move(pFilter);
3271 pImpl->m_aLogicName = rName;
3272 pImpl->m_nStorOpenMode = nOpenMode;
3273 Init_Impl();
3276 SfxMedium::SfxMedium( const uno::Sequence<beans::PropertyValue>& aArgs ) :
3277 pImpl(new SfxMedium_Impl)
3279 SfxAllItemSet *pParams = new SfxAllItemSet( SfxGetpApp()->GetPool() );
3280 pImpl->m_pSet.reset( pParams );
3281 TransformParameters( SID_OPENDOC, aArgs, *pParams );
3283 OUString aFilterProvider, aFilterName;
3285 const SfxPoolItem* pItem = nullptr;
3286 if (pImpl->m_pSet->HasItem(SID_FILTER_PROVIDER, &pItem))
3287 aFilterProvider = static_cast<const SfxStringItem*>(pItem)->GetValue();
3289 if (pImpl->m_pSet->HasItem(SID_FILTER_NAME, &pItem))
3290 aFilterName = static_cast<const SfxStringItem*>(pItem)->GetValue();
3293 if (aFilterProvider.isEmpty())
3295 // This is a conventional filter type.
3296 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( aFilterName );
3298 else
3300 // This filter is from an external provider such as orcus.
3301 pImpl->m_pCustomFilter.reset(new SfxFilter(aFilterProvider, aFilterName));
3302 pImpl->m_pFilter = pImpl->m_pCustomFilter;
3305 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
3306 if( pSalvageItem )
3308 // QUESTION: there is some treatment of Salvage in Init_Impl; align!
3309 if ( !pSalvageItem->GetValue().isEmpty() )
3311 // if a URL is provided in SalvageItem that means that the FileName refers to a temporary file
3312 // that must be copied here
3314 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
3315 if (!pFileNameItem) throw uno::RuntimeException();
3316 OUString aNewTempFileURL = SfxMedium::CreateTempCopyWithExt( pFileNameItem->GetValue() );
3317 if ( !aNewTempFileURL.isEmpty() )
3319 pImpl->m_pSet->Put( SfxStringItem( SID_FILE_NAME, aNewTempFileURL ) );
3320 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
3321 pImpl->m_pSet->ClearItem( SID_STREAM );
3322 pImpl->m_pSet->ClearItem( SID_CONTENT );
3324 else
3326 SAL_WARN( "sfx.doc", "Can not create a new temporary file for crash recovery!" );
3331 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
3332 if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
3333 pImpl->m_bOriginallyLoadedReadOnly = true;
3335 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
3336 if (!pFileNameItem) throw uno::RuntimeException();
3337 pImpl->m_aLogicName = pFileNameItem->GetValue();
3338 pImpl->m_nStorOpenMode = pImpl->m_bOriginallyLoadedReadOnly
3339 ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE;
3340 Init_Impl();
3344 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const std::shared_ptr<SfxItemSet>& p ) :
3345 pImpl(new SfxMedium_Impl)
3347 OUString aType = SfxFilter::GetTypeFromStorage(rStor);
3348 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( aType );
3349 DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
3351 Init_Impl();
3352 pImpl->xStorage = rStor;
3353 pImpl->bDisposeStorage = false;
3355 // always take BaseURL first, could be overwritten by ItemSet
3356 GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
3357 if ( p )
3358 GetItemSet()->Put( *p );
3362 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const OUString &rTypeName, const std::shared_ptr<SfxItemSet>& p ) :
3363 pImpl(new SfxMedium_Impl)
3365 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( rTypeName );
3366 DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
3368 Init_Impl();
3369 pImpl->xStorage = rStor;
3370 pImpl->bDisposeStorage = false;
3372 // always take BaseURL first, could be overwritten by ItemSet
3373 GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
3374 if ( p )
3375 GetItemSet()->Put( *p );
3379 SfxMedium::~SfxMedium()
3381 // if there is a requirement to clean the backup this is the last possibility to do it
3382 ClearBackup_Impl();
3384 Close(/*bInDestruction*/true);
3386 if( !pImpl->bIsTemp || pImpl->m_aName.isEmpty() )
3387 return;
3389 OUString aTemp;
3390 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aTemp )
3391 != osl::FileBase::E_None )
3393 SAL_WARN( "sfx.doc", "Physical name not convertible!");
3396 if ( !::utl::UCBContentHelper::Kill( aTemp ) )
3398 SAL_WARN( "sfx.doc", "Couldn't remove temporary file!");
3402 const OUString& SfxMedium::GetName() const
3404 return pImpl->m_aLogicName;
3407 const INetURLObject& SfxMedium::GetURLObject() const
3409 if (!pImpl->m_pURLObj)
3411 pImpl->m_pURLObj.reset( new INetURLObject( pImpl->m_aLogicName ) );
3412 pImpl->m_pURLObj->SetMark("");
3415 return *pImpl->m_pURLObj;
3418 void SfxMedium::SetExpired_Impl( const DateTime& rDateTime )
3420 pImpl->aExpireTime = rDateTime;
3424 bool SfxMedium::IsExpired() const
3426 return pImpl->aExpireTime.IsValidAndGregorian() && pImpl->aExpireTime < DateTime( DateTime::SYSTEM );
3430 SfxFrame* SfxMedium::GetLoadTargetFrame() const
3432 return pImpl->wLoadTargetFrame;
3435 void SfxMedium::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly )
3437 pImpl->m_xInputStreamToLoadFrom = xInputStream;
3438 pImpl->m_bInputStreamIsReadOnly = bIsReadOnly;
3441 void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame )
3443 pImpl->wLoadTargetFrame = pFrame;
3447 void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& rStor )
3449 pImpl->xStorage = rStor;
3453 SfxItemSet* SfxMedium::GetItemSet() const
3455 // this method *must* return an ItemSet, returning NULL can cause crashes
3456 if (!pImpl->m_pSet)
3457 pImpl->m_pSet.reset( new SfxAllItemSet( SfxGetpApp()->GetPool() ) );
3458 return pImpl->m_pSet.get();
3462 SvKeyValueIterator* SfxMedium::GetHeaderAttributes_Impl()
3464 if( !pImpl->xAttributes.is() )
3466 pImpl->xAttributes = SvKeyValueIteratorRef( new SvKeyValueIterator );
3468 if ( GetContent().is() )
3472 Any aAny = pImpl->aContent.getPropertyValue("MediaType");
3473 OUString aContentType;
3474 aAny >>= aContentType;
3476 pImpl->xAttributes->Append( SvKeyValue( "content-type", aContentType ) );
3478 catch ( const css::uno::Exception& )
3484 return pImpl->xAttributes.get();
3487 css::uno::Reference< css::io::XInputStream > const & SfxMedium::GetInputStream()
3489 if ( !pImpl->xInputStream.is() )
3490 GetMedium_Impl();
3491 return pImpl->xInputStream;
3494 const uno::Sequence < util::RevisionTag >& SfxMedium::GetVersionList( bool _bNoReload )
3496 // if the medium has no name, then this medium should represent a new document and can have no version info
3497 if ( ( !_bNoReload || !pImpl->m_bVersionsAlreadyLoaded ) && !pImpl->aVersions.hasElements() &&
3498 ( !pImpl->m_aName.isEmpty() || !pImpl->m_aLogicName.isEmpty() ) && GetStorage().is() )
3500 uno::Reference < document::XDocumentRevisionListPersistence > xReader =
3501 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3504 pImpl->aVersions = xReader->load( GetStorage() );
3506 catch ( const uno::Exception& )
3511 if ( !pImpl->m_bVersionsAlreadyLoaded )
3512 pImpl->m_bVersionsAlreadyLoaded = true;
3514 return pImpl->aVersions;
3517 uno::Sequence < util::RevisionTag > SfxMedium::GetVersionList( const uno::Reference < embed::XStorage >& xStorage )
3519 uno::Reference < document::XDocumentRevisionListPersistence > xReader =
3520 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3523 return xReader->load( xStorage );
3525 catch ( const uno::Exception& )
3529 return uno::Sequence < util::RevisionTag >();
3532 void SfxMedium::AddVersion_Impl( util::RevisionTag& rRevision )
3534 if ( !GetStorage().is() )
3535 return;
3537 // To determine a unique name for the stream
3538 std::vector<sal_uInt32> aLongs;
3539 sal_Int32 nLength = pImpl->aVersions.getLength();
3540 for ( const auto& rVersion : std::as_const(pImpl->aVersions) )
3542 sal_uInt32 nVer = static_cast<sal_uInt32>( rVersion.Identifier.copy(7).toInt32());
3543 size_t n;
3544 for ( n=0; n<aLongs.size(); ++n )
3545 if ( nVer<aLongs[n] )
3546 break;
3548 aLongs.insert( aLongs.begin()+n, nVer );
3551 std::vector<sal_uInt32>::size_type nKey;
3552 for ( nKey=0; nKey<aLongs.size(); ++nKey )
3553 if ( aLongs[nKey] > nKey+1 )
3554 break;
3556 OUString aRevName = "Version" + OUString::number( nKey + 1 );
3557 pImpl->aVersions.realloc( nLength+1 );
3558 rRevision.Identifier = aRevName;
3559 pImpl->aVersions[nLength] = rRevision;
3562 void SfxMedium::RemoveVersion_Impl( const OUString& rName )
3564 if ( !pImpl->aVersions.hasElements() )
3565 return;
3567 auto pVersion = std::find_if(pImpl->aVersions.begin(), pImpl->aVersions.end(),
3568 [&rName](const auto& rVersion) { return rVersion.Identifier == rName; });
3569 if (pVersion != pImpl->aVersions.end())
3571 auto nIndex = static_cast<sal_Int32>(std::distance(pImpl->aVersions.begin(), pVersion));
3572 comphelper::removeElementAt(pImpl->aVersions, nIndex);
3576 bool SfxMedium::TransferVersionList_Impl( SfxMedium const & rMedium )
3578 if ( rMedium.pImpl->aVersions.hasElements() )
3580 pImpl->aVersions = rMedium.pImpl->aVersions;
3581 return true;
3584 return false;
3587 void SfxMedium::SaveVersionList_Impl()
3589 if ( !GetStorage().is() )
3590 return;
3592 if ( !pImpl->aVersions.hasElements() )
3593 return;
3595 uno::Reference < document::XDocumentRevisionListPersistence > xWriter =
3596 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3599 xWriter->store( GetStorage(), pImpl->aVersions );
3601 catch ( const uno::Exception& )
3606 bool SfxMedium::IsReadOnly() const
3608 // a) ReadOnly filter can't produce read/write contents!
3609 bool bReadOnly = pImpl->m_pFilter && (pImpl->m_pFilter->GetFilterFlags() & SfxFilterFlags::OPENREADONLY);
3611 // b) if filter allow read/write contents .. check open mode of the storage
3612 if (!bReadOnly)
3613 bReadOnly = !( GetOpenMode() & StreamMode::WRITE );
3615 // c) the API can force the readonly state!
3616 if (!bReadOnly)
3618 const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOC_READONLY, false);
3619 if (pItem)
3620 bReadOnly = pItem->GetValue();
3623 return bReadOnly;
3626 bool SfxMedium::IsOriginallyReadOnly() const
3628 return pImpl->m_bOriginallyReadOnly;
3631 bool SfxMedium::IsOriginallyLoadedReadOnly() const
3633 return pImpl->m_bOriginallyLoadedReadOnly;
3636 bool SfxMedium::SetWritableForUserOnly( const OUString& aURL )
3638 // UCB does not allow to allow write access only for the user,
3639 // use osl API
3640 bool bResult = false;
3642 ::osl::DirectoryItem aDirItem;
3643 if ( ::osl::DirectoryItem::get( aURL, aDirItem ) == ::osl::FileBase::E_None )
3645 ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Attributes );
3646 if ( aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None
3647 && aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) )
3649 sal_uInt64 nAttributes = aFileStatus.getAttributes();
3651 nAttributes &= ~(osl_File_Attribute_OwnWrite |
3652 osl_File_Attribute_GrpWrite |
3653 osl_File_Attribute_OthWrite |
3654 osl_File_Attribute_ReadOnly);
3655 nAttributes |= (osl_File_Attribute_OwnWrite |
3656 osl_File_Attribute_OwnRead);
3658 bResult = ( osl::File::setAttributes( aURL, nAttributes ) == ::osl::FileBase::E_None );
3662 return bResult;
3665 namespace
3667 /// Get the parent directory of a temporary file for output purposes.
3668 OUString GetLogicBase(std::unique_ptr<SfxMedium_Impl> const & pImpl)
3670 OUString aLogicBase;
3672 // In a sandboxed environment we don't want to attempt to create temporary files in the same
3673 // directory where the user has selected an output file to be stored. The sandboxed process has
3674 // permission only to create the specifically named output file in that directory.
3675 #if !HAVE_FEATURE_MACOSX_SANDBOX
3677 if (comphelper::isFileUrl(pImpl->m_aLogicName) && !pImpl->m_pInStream)
3679 // Try to create the temp file in the same directory when storing.
3680 sal_Int32 nOffset = pImpl->m_aLogicName.lastIndexOf("/");
3681 if (nOffset != -1)
3682 aLogicBase = pImpl->m_aLogicName.copy(0, nOffset);
3683 if (aLogicBase == "file://")
3684 // Doesn't make sense.
3685 aLogicBase.clear();
3688 if (pImpl->m_bHasEmbeddedObjects)
3689 // Embedded objects would mean a special base, ignore that.
3690 aLogicBase.clear();
3692 #endif // !HAVE_FEATURE_MACOSX_SANDBOX
3694 return aLogicBase;
3698 void SfxMedium::CreateTempFile( bool bReplace )
3700 if ( pImpl->pTempFile )
3702 if ( !bReplace )
3703 return;
3705 pImpl->pTempFile.reset();
3706 pImpl->m_aName.clear();
3709 OUString aLogicBase = GetLogicBase(pImpl);
3710 pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) );
3711 pImpl->pTempFile->EnableKillingFile();
3712 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3713 OUString aTmpURL = pImpl->pTempFile->GetURL();
3714 if ( pImpl->m_aName.isEmpty() || aTmpURL.isEmpty() )
3716 SetError(ERRCODE_IO_CANTWRITE);
3717 return;
3720 if ( !(pImpl->m_nStorOpenMode & StreamMode::TRUNC) )
3722 bool bTransferSuccess = false;
3724 if ( GetContent().is()
3725 && GetURLObject().GetProtocol() == INetProtocol::File
3726 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
3728 // if there is already such a document, we should copy it
3729 // if it is a file system use OS copy process
3732 uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
3733 INetURLObject aTmpURLObj( aTmpURL );
3734 OUString aFileName = aTmpURLObj.getName( INetURLObject::LAST_SEGMENT,
3735 true,
3736 INetURLObject::DecodeMechanism::WithCharset );
3737 if ( !aFileName.isEmpty() && aTmpURLObj.removeSegment() )
3739 ::ucbhelper::Content aTargetContent( aTmpURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
3740 OUString sMimeType = pImpl->getFilterMimeType();
3741 aTargetContent.transferContent( pImpl->aContent, ::ucbhelper::InsertOperation::Copy, aFileName, NameClash::OVERWRITE, sMimeType );
3742 SetWritableForUserOnly( aTmpURL );
3743 bTransferSuccess = true;
3746 catch( const uno::Exception& )
3749 if ( bTransferSuccess )
3751 CloseOutStream();
3752 CloseInStream();
3756 if ( !bTransferSuccess && pImpl->m_pInStream )
3758 // the case when there is no URL-access available or this is a remote protocol
3759 // but there is an input stream
3760 GetOutStream();
3761 if ( pImpl->m_pOutStream )
3763 std::unique_ptr<char[]> pBuf(new char [8192]);
3764 ErrCode nErr = ERRCODE_NONE;
3766 pImpl->m_pInStream->Seek(0);
3767 pImpl->m_pOutStream->Seek(0);
3769 while( !pImpl->m_pInStream->eof() && nErr == ERRCODE_NONE )
3771 sal_uInt32 nRead = pImpl->m_pInStream->ReadBytes(pBuf.get(), 8192);
3772 nErr = pImpl->m_pInStream->GetError();
3773 pImpl->m_pOutStream->WriteBytes( pBuf.get(), nRead );
3776 bTransferSuccess = true;
3777 CloseInStream();
3779 CloseOutStream_Impl();
3781 else
3783 // Quite strange design, but currently it is expected that in this case no transfer happens
3784 // TODO/LATER: get rid of this inconsistent part of the call design
3785 bTransferSuccess = true;
3786 CloseInStream();
3789 if ( !bTransferSuccess )
3791 SetError(ERRCODE_IO_CANTWRITE);
3792 return;
3796 CloseStorage();
3800 void SfxMedium::CreateTempFileNoCopy()
3802 // this call always replaces the existing temporary file
3803 pImpl->pTempFile.reset();
3805 OUString aLogicBase = GetLogicBase(pImpl);
3806 pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) );
3807 pImpl->pTempFile->EnableKillingFile();
3808 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3809 if ( pImpl->m_aName.isEmpty() )
3811 SetError(ERRCODE_IO_CANTWRITE);
3812 return;
3815 CloseOutStream_Impl();
3816 CloseStorage();
3819 bool SfxMedium::SignDocumentContentUsingCertificate(bool bHasValidDocumentSignature,
3820 const Reference<XCertificate>& xCertificate)
3822 bool bChanges = false;
3824 if (IsOpen() || GetError())
3826 SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
3827 return bChanges;
3830 // The component should know if there was a valid document signature, since
3831 // it should show a warning in this case
3832 OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
3833 uno::Reference< security::XDocumentDigitalSignatures > xSigner(
3834 security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
3835 comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
3837 uno::Reference< embed::XStorage > xWriteableZipStor;
3839 // we can reuse the temporary file if there is one already
3840 CreateTempFile( false );
3841 GetMedium_Impl();
3845 if ( !pImpl->xStream.is() )
3846 throw uno::RuntimeException();
3848 bool bODF = GetFilter()->IsOwnFormat();
3851 xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
3853 catch (const io::IOException&)
3855 if (bODF)
3857 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
3861 if ( !xWriteableZipStor.is() && bODF )
3862 throw uno::RuntimeException();
3864 uno::Reference< embed::XStorage > xMetaInf;
3865 if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
3867 xMetaInf = xWriteableZipStor->openStorageElement(
3868 "META-INF",
3869 embed::ElementModes::READWRITE );
3870 if ( !xMetaInf.is() )
3871 throw uno::RuntimeException();
3875 if (xMetaInf.is())
3877 // ODF.
3878 uno::Reference< io::XStream > xStream;
3879 if (GetFilter() && GetFilter()->IsOwnFormat())
3880 xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
3882 bool bSuccess = xSigner->signDocumentWithCertificate(xCertificate, GetZipStorageToSign_Impl(), xStream);
3884 if (bSuccess)
3886 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
3887 xTransact->commit();
3888 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
3889 xTransact->commit();
3891 // the temporary file has been written, commit it to the original file
3892 Commit();
3893 bChanges = true;
3896 else if (xWriteableZipStor.is())
3898 // OOXML.
3899 uno::Reference<io::XStream> xStream;
3901 // We need read-write to be able to add the signature relation.
3902 bool bSuccess =xSigner->signDocumentWithCertificate(
3903 xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
3905 if (bSuccess)
3907 uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
3908 xTransact->commit();
3910 // the temporary file has been written, commit it to the original file
3911 Commit();
3912 bChanges = true;
3915 else
3917 // Something not ZIP based: e.g. PDF.
3918 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
3919 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
3920 if (xSigner->signDocumentWithCertificate(xCertificate, uno::Reference<embed::XStorage>(), xStream))
3921 bChanges = true;
3925 catch ( const uno::Exception& )
3927 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
3930 CloseAndRelease();
3932 ResetError();
3934 return bChanges;
3937 bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent,
3938 bool bSignScriptingContent,
3939 bool bHasValidDocumentSignature,
3940 const OUString& aSignatureLineId,
3941 const Reference<XCertificate>& xCert,
3942 const Reference<XGraphic>& xValidGraphic,
3943 const Reference<XGraphic>& xInvalidGraphic,
3944 const OUString& aComment)
3946 bool bChanges = false;
3948 if (IsOpen() || GetError())
3950 SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
3951 return bChanges;
3954 // The component should know if there was a valid document signature, since
3955 // it should show a warning in this case
3956 OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
3957 uno::Reference< security::XDocumentDigitalSignatures > xSigner(
3958 security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
3959 comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
3960 if (pDialogParent)
3961 xSigner->setParentWindow(pDialogParent->GetXWindow());
3963 uno::Reference< embed::XStorage > xWriteableZipStor;
3965 // we can reuse the temporary file if there is one already
3966 CreateTempFile( false );
3967 GetMedium_Impl();
3971 if ( !pImpl->xStream.is() )
3972 throw uno::RuntimeException();
3974 bool bODF = GetFilter()->IsOwnFormat();
3977 xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
3979 catch (const io::IOException&)
3981 if (bODF)
3983 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
3987 if ( !xWriteableZipStor.is() && bODF )
3988 throw uno::RuntimeException();
3990 uno::Reference< embed::XStorage > xMetaInf;
3991 if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
3993 xMetaInf = xWriteableZipStor->openStorageElement(
3994 "META-INF",
3995 embed::ElementModes::READWRITE );
3996 if ( !xMetaInf.is() )
3997 throw uno::RuntimeException();
4000 if ( bSignScriptingContent )
4002 // If the signature has already the document signature it will be removed
4003 // after the scripting signature is inserted.
4004 uno::Reference< io::XStream > xStream(
4005 xMetaInf->openStreamElement( xSigner->getScriptingContentSignatureDefaultStreamName(),
4006 embed::ElementModes::READWRITE ),
4007 uno::UNO_SET_THROW );
4009 if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), xStream ) )
4011 // remove the document signature if any
4012 OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName();
4013 if ( !aDocSigName.isEmpty() && xMetaInf->hasByName( aDocSigName ) )
4014 xMetaInf->removeElement( aDocSigName );
4016 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
4017 xTransact->commit();
4018 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
4019 xTransact->commit();
4021 // the temporary file has been written, commit it to the original file
4022 Commit();
4023 bChanges = true;
4026 else
4028 if (xMetaInf.is())
4030 // ODF.
4031 uno::Reference< io::XStream > xStream;
4032 if (GetFilter() && GetFilter()->IsOwnFormat())
4033 xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
4035 bool bSuccess = false;
4036 if (xCert.is())
4037 bSuccess = xSigner->signSignatureLine(
4038 GetZipStorageToSign_Impl(), xStream, aSignatureLineId, xCert,
4039 xValidGraphic, xInvalidGraphic, aComment);
4040 else
4041 bSuccess = xSigner->signDocumentContent(GetZipStorageToSign_Impl(),
4042 xStream);
4044 if (bSuccess)
4046 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
4047 xTransact->commit();
4048 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
4049 xTransact->commit();
4051 // the temporary file has been written, commit it to the original file
4052 Commit();
4053 bChanges = true;
4056 else if (xWriteableZipStor.is())
4058 // OOXML.
4059 uno::Reference<io::XStream> xStream;
4061 bool bSuccess = false;
4062 if (xCert.is())
4064 bSuccess = xSigner->signSignatureLine(
4065 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream, aSignatureLineId,
4066 xCert, xValidGraphic, xInvalidGraphic, aComment);
4068 else
4070 // We need read-write to be able to add the signature relation.
4071 bSuccess =xSigner->signDocumentContent(
4072 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
4075 if (bSuccess)
4077 uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
4078 xTransact->commit();
4080 // the temporary file has been written, commit it to the original file
4081 Commit();
4082 bChanges = true;
4085 else
4087 // Something not ZIP based: e.g. PDF.
4088 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
4089 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
4090 if (xSigner->signDocumentContent(uno::Reference<embed::XStorage>(), xStream))
4091 bChanges = true;
4095 catch ( const uno::Exception& )
4097 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
4100 CloseAndRelease();
4102 ResetError();
4104 return bChanges;
4108 SignatureState SfxMedium::GetCachedSignatureState_Impl() const
4110 return pImpl->m_nSignatureState;
4114 void SfxMedium::SetCachedSignatureState_Impl( SignatureState nState )
4116 pImpl->m_nSignatureState = nState;
4119 void SfxMedium::SetHasEmbeddedObjects(bool bHasEmbeddedObjects)
4121 pImpl->m_bHasEmbeddedObjects = bHasEmbeddedObjects;
4124 bool SfxMedium::HasStorage_Impl() const
4126 return pImpl->xStorage.is();
4129 bool SfxMedium::IsOpen() const
4131 return pImpl->m_pInStream || pImpl->m_pOutStream || pImpl->xStorage.is();
4134 OUString SfxMedium::CreateTempCopyWithExt( const OUString& aURL )
4136 OUString aResult;
4138 if ( !aURL.isEmpty() )
4140 sal_Int32 nPrefixLen = aURL.lastIndexOf( '.' );
4141 OUString aExt = ( nPrefixLen == -1 ) ? OUString() : aURL.copy( nPrefixLen );
4143 OUString aNewTempFileURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
4144 if ( !aNewTempFileURL.isEmpty() )
4146 INetURLObject aSource( aURL );
4147 INetURLObject aDest( aNewTempFileURL );
4148 OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT,
4149 true,
4150 INetURLObject::DecodeMechanism::WithCharset );
4151 if ( !aFileName.isEmpty() && aDest.removeSegment() )
4155 uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
4156 ::ucbhelper::Content aTargetContent( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
4157 ::ucbhelper::Content aSourceContent( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
4158 aTargetContent.transferContent( aSourceContent,
4159 ::ucbhelper::InsertOperation::Copy,
4160 aFileName,
4161 NameClash::OVERWRITE );
4162 aResult = aNewTempFileURL;
4164 catch( const uno::Exception& )
4170 return aResult;
4173 bool SfxMedium::CallApproveHandler(const uno::Reference< task::XInteractionHandler >& xHandler, const uno::Any& rRequest, bool bAllowAbort)
4175 bool bResult = false;
4177 if ( xHandler.is() )
4181 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( bAllowAbort ? 2 : 1 );
4183 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
4184 aContinuations[ 0 ] = pApprove.get();
4186 if ( bAllowAbort )
4188 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( new ::comphelper::OInteractionAbort );
4189 aContinuations[ 1 ] = pAbort.get();
4192 xHandler->handle(::framework::InteractionRequest::CreateRequest(rRequest, aContinuations));
4193 bResult = pApprove->wasSelected();
4195 catch( const Exception& )
4200 return bResult;
4203 OUString SfxMedium::SwitchDocumentToTempFile()
4205 // the method returns empty string in case of failure
4206 OUString aResult;
4207 OUString aOrigURL = pImpl->m_aLogicName;
4209 if ( !aOrigURL.isEmpty() )
4211 sal_Int32 nPrefixLen = aOrigURL.lastIndexOf( '.' );
4212 OUString const aExt = (nPrefixLen == -1)
4213 ? OUString()
4214 : aOrigURL.copy(nPrefixLen);
4215 OUString aNewURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
4217 // TODO/LATER: In future the aLogicName should be set to shared folder URL
4218 // and a temporary file should be created. Transport_Impl should be impossible then.
4219 if ( !aNewURL.isEmpty() )
4221 uno::Reference< embed::XStorage > xStorage = GetStorage();
4222 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
4224 if ( xOptStorage.is() )
4226 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4227 CanDisposeStorage_Impl( false );
4228 Close();
4229 SetPhysicalName_Impl( OUString() );
4230 SetName( aNewURL );
4232 // remove the readonly state
4233 bool bWasReadonly = false;
4234 pImpl->m_nStorOpenMode = SFX_STREAM_READWRITE;
4235 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
4236 if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
4237 bWasReadonly = true;
4238 GetItemSet()->ClearItem( SID_DOC_READONLY );
4240 GetMedium_Impl();
4241 LockOrigFileOnDemand( false, false );
4242 CreateTempFile();
4243 GetMedium_Impl();
4245 if ( pImpl->xStream.is() )
4249 xOptStorage->writeAndAttachToStream( pImpl->xStream );
4250 pImpl->xStorage = xStorage;
4251 aResult = aNewURL;
4253 catch( const uno::Exception& )
4257 if (bWasReadonly)
4259 // set the readonly state back
4260 pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
4261 GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
4264 if ( aResult.isEmpty() )
4266 Close();
4267 SetPhysicalName_Impl( OUString() );
4268 SetName( aOrigURL );
4269 GetMedium_Impl();
4270 pImpl->xStorage = xStorage;
4276 return aResult;
4279 bool SfxMedium::SwitchDocumentToFile( const OUString& aURL )
4281 // the method is only for storage based documents
4282 bool bResult = false;
4283 OUString aOrigURL = pImpl->m_aLogicName;
4285 if ( !aURL.isEmpty() && !aOrigURL.isEmpty() )
4287 uno::Reference< embed::XStorage > xStorage = GetStorage();
4288 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
4290 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4291 CanDisposeStorage_Impl( false );
4292 Close();
4293 SetPhysicalName_Impl( OUString() );
4294 SetName( aURL );
4296 // open the temporary file based document
4297 GetMedium_Impl();
4298 LockOrigFileOnDemand( false, false );
4299 CreateTempFile();
4300 GetMedium_Impl();
4302 if ( pImpl->xStream.is() )
4306 uno::Reference< io::XTruncate > xTruncate( pImpl->xStream, uno::UNO_QUERY_THROW );
4307 xTruncate->truncate();
4308 if ( xOptStorage.is() )
4309 xOptStorage->writeAndAttachToStream( pImpl->xStream );
4310 pImpl->xStorage = xStorage;
4311 bResult = true;
4313 catch( const uno::Exception& )
4317 if ( !bResult )
4319 Close();
4320 SetPhysicalName_Impl( OUString() );
4321 SetName( aOrigURL );
4322 GetMedium_Impl();
4323 pImpl->xStorage = xStorage;
4327 return bResult;
4330 void SfxMedium::SetInCheckIn( bool bInCheckIn )
4332 pImpl->m_bInCheckIn = bInCheckIn;
4335 bool SfxMedium::IsInCheckIn( ) const
4337 return pImpl->m_bInCheckIn;
4340 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */