Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / doc / docfile.cxx
blobc56606a7859eb5fd000bee16fe3b2c07275725a9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #ifdef UNX
23 #include <sys/stat.h>
24 #endif
26 #include <sfx2/docfile.hxx>
27 #include <sfx2/signaturestate.hxx>
29 #include <com/sun/star/task/InteractionHandler.hpp>
30 #include <com/sun/star/task/XStatusIndicator.hpp>
31 #include <com/sun/star/uno/Reference.h>
32 #include <com/sun/star/ucb/XContent.hpp>
33 #include <com/sun/star/container/XChild.hpp>
34 #include <com/sun/star/document/XDocumentRevisionListPersistence.hpp>
35 #include <com/sun/star/document/LockedDocumentRequest.hpp>
36 #include <com/sun/star/document/LockedOnSavingRequest.hpp>
37 #include <com/sun/star/document/OwnLockOnDocumentRequest.hpp>
38 #include <com/sun/star/document/LockFileIgnoreRequest.hpp>
39 #include <com/sun/star/document/LockFileCorruptRequest.hpp>
40 #include <com/sun/star/document/ChangedByOthersRequest.hpp>
41 #include <com/sun/star/embed/XTransactedObject.hpp>
42 #include <com/sun/star/embed/ElementModes.hpp>
43 #include <com/sun/star/embed/UseBackupException.hpp>
44 #include <com/sun/star/embed/XOptimizedStorage.hpp>
45 #include <com/sun/star/frame/XModel.hpp>
46 #include <com/sun/star/graphic/XGraphic.hpp>
47 #include <com/sun/star/ucb/ContentCreationException.hpp>
48 #include <com/sun/star/ucb/InteractiveIOException.hpp>
49 #include <com/sun/star/ucb/CommandFailedException.hpp>
50 #include <com/sun/star/ucb/CommandAbortedException.hpp>
51 #include <com/sun/star/ucb/InteractiveLockingLockedException.hpp>
52 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
53 #include <com/sun/star/ucb/Lock.hpp>
54 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
55 #include <com/sun/star/ucb/XProgressHandler.hpp>
56 #include <com/sun/star/io/XOutputStream.hpp>
57 #include <com/sun/star/io/XInputStream.hpp>
58 #include <com/sun/star/io/XTruncate.hpp>
59 #include <com/sun/star/io/XSeekable.hpp>
60 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
61 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
62 #include <com/sun/star/ucb/NameClash.hpp>
63 #include <com/sun/star/beans/NamedValue.hpp>
64 #include <com/sun/star/beans/PropertyValue.hpp>
65 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
66 #include <com/sun/star/security/XCertificate.hpp>
67 #include <tools/urlobj.hxx>
68 #include <tools/fileutil.hxx>
69 #include <unotools/configmgr.hxx>
70 #include <unotools/tempfile.hxx>
71 #include <comphelper/fileurl.hxx>
72 #include <comphelper/processfactory.hxx>
73 #include <comphelper/interaction.hxx>
74 #include <comphelper/sequence.hxx>
75 #include <comphelper/simplefileaccessinteraction.hxx>
76 #include <framework/interaction.hxx>
77 #include <utility>
78 #include <svl/stritem.hxx>
79 #include <svl/eitem.hxx>
80 #include <svtools/sfxecode.hxx>
81 #include <svl/itemset.hxx>
82 #include <svl/intitem.hxx>
83 #include <svtools/svparser.hxx>
84 #include <sal/log.hxx>
86 #include <unotools/streamwrap.hxx>
88 #include <osl/file.hxx>
90 #include <comphelper/storagehelper.hxx>
91 #include <unotools/mediadescriptor.hxx>
92 #include <comphelper/docpasswordhelper.hxx>
93 #include <tools/datetime.hxx>
94 #include <unotools/pathoptions.hxx>
95 #include <svtools/asynclink.hxx>
96 #include <ucbhelper/commandenvironment.hxx>
97 #include <unotools/ucbstreamhelper.hxx>
98 #include <unotools/ucbhelper.hxx>
99 #include <unotools/progresshandlerwrap.hxx>
100 #include <ucbhelper/content.hxx>
101 #include <ucbhelper/interactionrequest.hxx>
102 #include <sot/storage.hxx>
103 #include <unotools/saveopt.hxx>
104 #include <svl/documentlockfile.hxx>
105 #include <svl/msodocumentlockfile.hxx>
106 #include <com/sun/star/document/DocumentRevisionListPersistence.hpp>
108 #include <sfx2/app.hxx>
109 #include <sfx2/frame.hxx>
110 #include <sfx2/fcontnr.hxx>
111 #include <sfx2/docfilt.hxx>
112 #include <sfx2/sfxsids.hrc>
113 #include <sfx2/sfxuno.hxx>
114 #include <openflag.hxx>
115 #include <officecfg/Office/Common.hxx>
116 #include <comphelper/propertysequence.hxx>
117 #include <vcl/weld.hxx>
118 #include <vcl/svapp.hxx>
119 #include <tools/diagnose_ex.h>
120 #include <unotools/fltrcfg.hxx>
121 #include <sfx2/digitalsignatures.hxx>
123 #include <com/sun/star/io/WrongFormatException.hpp>
125 #include <memory>
127 using namespace ::com::sun::star;
128 using namespace ::com::sun::star::graphic;
129 using namespace ::com::sun::star::uno;
130 using namespace ::com::sun::star::ucb;
131 using namespace ::com::sun::star::beans;
132 using namespace ::com::sun::star::io;
133 using namespace ::com::sun::star::security;
135 namespace {
137 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
139 bool IsSystemFileLockingUsed()
141 #if HAVE_FEATURE_MACOSX_SANDBOX
142 return true;
143 #else
144 return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
145 #endif
149 bool IsOOoLockFileUsed()
151 #if HAVE_FEATURE_MACOSX_SANDBOX
152 return false;
153 #else
154 return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get();
155 #endif
158 bool IsLockingUsed()
160 return officecfg::Office::Common::Misc::UseLocking::get();
163 #endif
165 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
166 bool IsWebDAVLockingUsed()
168 return officecfg::Office::Common::Misc::UseWebDAVFileLocking::get();
170 #endif
172 /// Gets default attributes of a file:// URL.
173 sal_uInt64 GetDefaultFileAttributes(const OUString& rURL)
175 sal_uInt64 nRet = 0;
177 if (!comphelper::isFileUrl(rURL))
178 return nRet;
180 // Make sure the file exists (and create it if not).
181 osl::File aFile(rURL);
182 osl::File::RC nRes = aFile.open(osl_File_OpenFlag_Create);
183 if (nRes != osl::File::E_None && nRes != osl::File::E_EXIST)
184 return nRet;
186 aFile.close();
188 osl::DirectoryItem aItem;
189 if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
190 return nRet;
192 osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
193 if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
194 return nRet;
196 nRet = aStatus.getAttributes();
197 return nRet;
200 /// Determines if rURL is safe to move or not.
201 bool IsFileMovable(const INetURLObject& rURL)
203 #ifdef MACOSX
204 (void)rURL;
205 // Hide extension macOS-specific file property would be lost.
206 return false;
207 #else
209 if (rURL.GetProtocol() != INetProtocol::File)
210 // Not a file:// URL.
211 return false;
213 #ifdef UNX
214 OUString sPath = rURL.getFSysPath(FSysStyle::Unix);
215 if (sPath.isEmpty())
216 return false;
218 struct stat buf;
219 if (lstat(sPath.toUtf8().getStr(), &buf) != 0)
220 return false;
222 // Hardlink or symlink: osl::File::move() doesn't play with these nicely.
223 if (buf.st_nlink > 1 || S_ISLNK(buf.st_mode))
224 return false;
225 #elif defined _WIN32
226 if (tools::IsMappedWebDAVPath(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)))
227 return false;
228 #endif
230 return true;
231 #endif
234 } // anonymous namespace
236 class SfxMedium_Impl
238 public:
239 StreamMode m_nStorOpenMode;
240 ErrCode m_eError;
242 ::ucbhelper::Content aContent;
243 bool bUpdatePickList:1;
244 bool bIsTemp:1;
245 bool bDownloadDone:1;
246 bool bIsStorage:1;
247 bool bUseInteractionHandler:1;
248 bool bAllowDefaultIntHdl:1;
249 bool bDisposeStorage:1;
250 bool bStorageBasedOnInStream:1;
251 bool m_bSalvageMode:1;
252 bool m_bVersionsAlreadyLoaded:1;
253 bool m_bLocked:1;
254 bool m_bMSOLockFileCreated : 1;
255 bool m_bDisableUnlockWebDAV:1;
256 bool m_bGotDateTime:1;
257 bool m_bRemoveBackup:1;
258 bool m_bOriginallyReadOnly:1;
259 bool m_bOriginallyLoadedReadOnly:1;
260 bool m_bTriedStorage:1;
261 bool m_bRemote:1;
262 bool m_bInputStreamIsReadOnly:1;
263 bool m_bInCheckIn:1;
264 bool m_bDisableFileSync = false;
266 OUString m_aName;
267 OUString m_aLogicName;
268 OUString m_aLongName;
270 mutable std::shared_ptr<SfxItemSet> m_pSet;
271 mutable std::unique_ptr<INetURLObject> m_pURLObj;
273 std::shared_ptr<const SfxFilter> m_pFilter;
274 std::shared_ptr<const SfxFilter> m_pCustomFilter;
276 std::unique_ptr<SvStream> m_pInStream;
277 std::unique_ptr<SvStream> m_pOutStream;
279 OUString aOrigURL;
280 DateTime aExpireTime;
281 SfxFrameWeakRef wLoadTargetFrame;
282 SvKeyValueIteratorRef xAttributes;
284 svtools::AsynchronLink aDoneLink;
286 uno::Sequence < util::RevisionTag > aVersions;
288 std::unique_ptr<::utl::TempFile> pTempFile;
290 uno::Reference<embed::XStorage> xStorage;
291 uno::Reference<embed::XStorage> m_xZipStorage;
292 uno::Reference<io::XInputStream> m_xInputStreamToLoadFrom;
293 uno::Reference<io::XInputStream> xInputStream;
294 uno::Reference<io::XStream> xStream;
295 uno::Reference<io::XStream> m_xLockingStream;
296 uno::Reference<task::XInteractionHandler> xInteraction;
298 ErrCode nLastStorageError;
300 OUString m_aBackupURL;
302 // the following member is changed and makes sense only during saving
303 // TODO/LATER: in future the signature state should be controlled by the medium not by the document
304 // in this case the member will hold this information
305 SignatureState m_nSignatureState;
307 bool m_bHasEmbeddedObjects = false;
309 util::DateTime m_aDateTime;
311 uno::Sequence<beans::PropertyValue> m_aArgs;
313 explicit SfxMedium_Impl();
314 ~SfxMedium_Impl();
315 SfxMedium_Impl(const SfxMedium_Impl&) = delete;
316 SfxMedium_Impl& operator=(const SfxMedium_Impl&) = delete;
318 OUString getFilterMimeType() const
319 { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); }
323 SfxMedium_Impl::SfxMedium_Impl() :
324 m_nStorOpenMode(SFX_STREAM_READWRITE),
325 m_eError(ERRCODE_NONE),
326 bUpdatePickList(true),
327 bIsTemp( false ),
328 bDownloadDone( true ),
329 bIsStorage( false ),
330 bUseInteractionHandler( true ),
331 bAllowDefaultIntHdl( false ),
332 bDisposeStorage( false ),
333 bStorageBasedOnInStream( false ),
334 m_bSalvageMode( false ),
335 m_bVersionsAlreadyLoaded( false ),
336 m_bLocked( false ),
337 m_bMSOLockFileCreated( false ),
338 m_bDisableUnlockWebDAV( false ),
339 m_bGotDateTime( false ),
340 m_bRemoveBackup( false ),
341 m_bOriginallyReadOnly(false),
342 m_bOriginallyLoadedReadOnly(false),
343 m_bTriedStorage(false),
344 m_bRemote(false),
345 m_bInputStreamIsReadOnly(false),
346 m_bInCheckIn(false),
347 aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ),
348 nLastStorageError( ERRCODE_NONE ),
349 m_nSignatureState( SignatureState::NOSIGNATURES )
351 aDoneLink.CreateMutex();
355 SfxMedium_Impl::~SfxMedium_Impl()
357 aDoneLink.ClearPendingCall();
359 pTempFile.reset();
360 m_pSet.reset();
361 m_pURLObj.reset();
364 void SfxMedium::ResetError()
366 pImpl->m_eError = ERRCODE_NONE;
367 if( pImpl->m_pInStream )
368 pImpl->m_pInStream->ResetError();
369 if( pImpl->m_pOutStream )
370 pImpl->m_pOutStream->ResetError();
373 ErrCode const & SfxMedium::GetLastStorageCreationState() const
375 return pImpl->nLastStorageError;
378 void SfxMedium::SetError(ErrCode nError)
380 pImpl->m_eError = nError;
383 ErrCode SfxMedium::GetErrorCode() const
385 ErrCode lError = pImpl->m_eError;
386 if(!lError && pImpl->m_pInStream)
387 lError = pImpl->m_pInStream->GetErrorCode();
388 if(!lError && pImpl->m_pOutStream)
389 lError = pImpl->m_pOutStream->GetErrorCode();
390 return lError;
393 void SfxMedium::CheckFileDate( const util::DateTime& aInitDate )
395 GetInitFileDate( true );
396 if ( pImpl->m_aDateTime.Seconds == aInitDate.Seconds
397 && pImpl->m_aDateTime.Minutes == aInitDate.Minutes
398 && pImpl->m_aDateTime.Hours == aInitDate.Hours
399 && pImpl->m_aDateTime.Day == aInitDate.Day
400 && pImpl->m_aDateTime.Month == aInitDate.Month
401 && pImpl->m_aDateTime.Year == aInitDate.Year )
402 return;
404 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
406 if ( !xHandler.is() )
407 return;
411 ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
412 document::ChangedByOthersRequest() ) );
413 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( 3 );
414 aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
415 aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
416 xInteractionRequestImpl->setContinuations( aContinuations );
418 xHandler->handle( xInteractionRequestImpl.get() );
420 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
421 if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
423 SetError(ERRCODE_ABORT);
426 catch ( const uno::Exception& )
430 bool SfxMedium::DocNeedsFileDateCheck() const
432 return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File ||
433 GetURLObject().isAnyKnownWebDAVScheme() ) );
436 util::DateTime const & SfxMedium::GetInitFileDate( bool bIgnoreOldValue )
438 if ( ( bIgnoreOldValue || !pImpl->m_bGotDateTime ) && !pImpl->m_aLogicName.isEmpty() )
442 // add a default css::ucb::XCommandEnvironment
443 // in order to have the WebDAV UCP provider manage http/https authentication correctly
444 ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
445 utl::UCBContentHelper::getDefaultCommandEnvironment(),
446 comphelper::getProcessComponentContext() );
448 aContent.getPropertyValue("DateModified") >>= pImpl->m_aDateTime;
449 pImpl->m_bGotDateTime = true;
451 catch ( const css::uno::Exception& )
456 return pImpl->m_aDateTime;
460 Reference < XContent > SfxMedium::GetContent() const
462 if ( !pImpl->aContent.get().is() )
464 Reference < css::ucb::XContent > xContent;
466 // tdf#95144 add a default css::ucb::XCommandEnvironment
467 // in order to have the WebDAV UCP provider manage https protocol certificates correctly
468 css:: uno::Reference< task::XInteractionHandler > xIH(
469 css::task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr ) );
471 css::uno::Reference< css::ucb::XProgressHandler > xProgress;
472 ::ucbhelper::CommandEnvironment* pCommandEnv = new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
474 Reference < css::ucb::XCommandEnvironment > xEnv( static_cast< css::ucb::XCommandEnvironment* >(pCommandEnv), css::uno::UNO_QUERY );
476 const SfxUnoAnyItem* pItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_CONTENT, false);
477 if ( pItem )
478 pItem->GetValue() >>= xContent;
480 if ( xContent.is() )
484 pImpl->aContent = ::ucbhelper::Content( xContent, xEnv, comphelper::getProcessComponentContext() );
486 catch ( const Exception& )
490 else
492 // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's used.");
493 OUString aURL;
494 if ( !pImpl->m_aName.isEmpty() )
495 osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL );
496 else if ( !pImpl->m_aLogicName.isEmpty() )
497 aURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
498 if (!aURL.isEmpty() )
499 (void)::ucbhelper::Content::create( aURL, xEnv, comphelper::getProcessComponentContext(), pImpl->aContent );
503 return pImpl->aContent.get();
506 OUString SfxMedium::GetBaseURL( bool bForSaving )
508 OUString aBaseURL;
509 const SfxStringItem* pBaseURLItem = GetItemSet()->GetItem<SfxStringItem>(SID_DOC_BASEURL);
510 if ( pBaseURLItem )
511 aBaseURL = pBaseURLItem->GetValue();
512 else if (!utl::ConfigManager::IsFuzzing() && GetContent().is())
516 Any aAny = pImpl->aContent.getPropertyValue("BaseURI");
517 aAny >>= aBaseURL;
519 catch ( const css::uno::Exception& )
523 if ( aBaseURL.isEmpty() )
524 aBaseURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
527 if ( bForSaving )
529 SvtSaveOptions aOpt;
530 bool bIsRemote = IsRemote();
531 if( (bIsRemote && !aOpt.IsSaveRelINet()) || (!pImpl->m_bRemote && !aOpt.IsSaveRelFSys()) )
532 return OUString();
535 return aBaseURL;
538 bool SfxMedium::IsSkipImages() const
540 const SfxStringItem* pSkipImagesItem = GetItemSet()->GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS);
541 return pSkipImagesItem && pSkipImagesItem->GetValue() == "SkipImages";
544 SvStream* SfxMedium::GetInStream()
546 if ( pImpl->m_pInStream )
547 return pImpl->m_pInStream.get();
549 if ( pImpl->pTempFile )
551 pImpl->m_pInStream.reset( new SvFileStream(pImpl->m_aName, pImpl->m_nStorOpenMode) );
553 pImpl->m_eError = pImpl->m_pInStream->GetError();
555 if (!pImpl->m_eError && (pImpl->m_nStorOpenMode & StreamMode::WRITE)
556 && ! pImpl->m_pInStream->IsWritable() )
558 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
559 pImpl->m_pInStream.reset();
561 else
562 return pImpl->m_pInStream.get();
565 GetMedium_Impl();
567 if ( GetError() )
568 return nullptr;
570 return pImpl->m_pInStream.get();
574 void SfxMedium::CloseInStream()
576 CloseInStream_Impl();
579 void SfxMedium::CloseInStream_Impl(bool bInDestruction)
581 // if there is a storage based on the InStream, we have to
582 // close the storage, too, because otherwise the storage
583 // would use an invalid ( deleted ) stream.
584 if ( pImpl->m_pInStream && pImpl->xStorage.is() )
586 if ( pImpl->bStorageBasedOnInStream )
587 CloseStorage();
590 if ( pImpl->m_pInStream && !GetContent().is() && !bInDestruction )
592 CreateTempFile();
593 return;
596 pImpl->m_pInStream.reset();
597 if ( pImpl->m_pSet )
598 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
600 CloseZipStorage_Impl();
601 pImpl->xInputStream.clear();
603 if ( !pImpl->m_pOutStream )
605 // output part of the stream is not used so the whole stream can be closed
606 // TODO/LATER: is it correct?
607 pImpl->xStream.clear();
608 if ( pImpl->m_pSet )
609 pImpl->m_pSet->ClearItem( SID_STREAM );
614 SvStream* SfxMedium::GetOutStream()
616 if ( !pImpl->m_pOutStream )
618 // Create a temp. file if there is none because we always
619 // need one.
620 CreateTempFile( false );
622 if ( pImpl->pTempFile )
624 // On windows we try to re-use XOutStream from xStream if that exists;
625 // because opening new SvFileStream in this situation may fail with ERROR_SHARING_VIOLATION
626 // TODO: this is a horrible hack that should probably be removed,
627 // somebody needs to investigate this more thoroughly...
628 if (getenv("SFX_MEDIUM_REUSE_STREAM") && pImpl->xStream.is())
630 assert(pImpl->xStream->getOutputStream().is()); // need that...
631 pImpl->m_pOutStream = utl::UcbStreamHelper::CreateStream(
632 pImpl->xStream, false);
634 else
636 // On Unix don't try to re-use XOutStream from xStream if that exists;
637 // it causes fdo#59022 (fails opening files via SMB on Linux)
638 pImpl->m_pOutStream.reset( new SvFileStream(
639 pImpl->m_aName, StreamMode::STD_READWRITE) );
641 CloseStorage();
645 return pImpl->m_pOutStream.get();
649 void SfxMedium::CloseOutStream()
651 CloseOutStream_Impl();
654 void SfxMedium::CloseOutStream_Impl()
656 if ( pImpl->m_pOutStream )
658 // if there is a storage based on the OutStream, we have to
659 // close the storage, too, because otherwise the storage
660 // would use an invalid ( deleted ) stream.
661 //TODO/MBA: how to deal with this?!
662 //maybe we need a new flag when the storage was created from the outstream
663 if ( pImpl->xStorage.is() )
665 CloseStorage();
668 pImpl->m_pOutStream.reset();
671 if ( !pImpl->m_pInStream )
673 // input part of the stream is not used so the whole stream can be closed
674 // TODO/LATER: is it correct?
675 pImpl->xStream.clear();
676 if ( pImpl->m_pSet )
677 pImpl->m_pSet->ClearItem( SID_STREAM );
682 const OUString& SfxMedium::GetPhysicalName() const
684 if ( pImpl->m_aName.isEmpty() && !pImpl->m_aLogicName.isEmpty() )
685 const_cast<SfxMedium*>(this)->CreateFileStream();
687 // return the name then
688 return pImpl->m_aName;
692 void SfxMedium::CreateFileStream()
694 // force synchron
695 if( pImpl->m_pInStream )
697 SvLockBytes* pBytes = pImpl->m_pInStream->GetLockBytes();
698 if( pBytes )
699 pBytes->SetSynchronMode();
702 GetInStream();
703 if( pImpl->m_pInStream )
705 CreateTempFile( false );
706 pImpl->bIsTemp = true;
707 CloseInStream_Impl();
712 bool SfxMedium::Commit()
714 if( pImpl->xStorage.is() )
715 StorageCommit_Impl();
716 else if( pImpl->m_pOutStream )
717 pImpl->m_pOutStream->Flush();
718 else if( pImpl->m_pInStream )
719 pImpl->m_pInStream->Flush();
721 if ( GetError() == ERRCODE_NONE )
723 // does something only in case there is a temporary file ( means aName points to different location than aLogicName )
724 Transfer_Impl();
727 bool bResult = ( GetError() == ERRCODE_NONE );
729 if ( bResult && DocNeedsFileDateCheck() )
730 GetInitFileDate( true );
732 // remove truncation mode from the flags
733 pImpl->m_nStorOpenMode &= ~StreamMode::TRUNC;
734 return bResult;
738 bool SfxMedium::IsStorage()
740 if ( pImpl->xStorage.is() )
741 return true;
743 if ( pImpl->m_bTriedStorage )
744 return pImpl->bIsStorage;
746 if ( pImpl->pTempFile )
748 OUString aURL;
749 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL )
750 != osl::FileBase::E_None )
752 SAL_WARN( "sfx.doc", "Physical name '" << pImpl->m_aName << "' not convertible to file URL");
754 pImpl->bIsStorage = SotStorage::IsStorageFile( aURL ) && !SotStorage::IsOLEStorage( aURL);
755 if ( !pImpl->bIsStorage )
756 pImpl->m_bTriedStorage = true;
758 else if ( GetInStream() )
760 pImpl->bIsStorage = SotStorage::IsStorageFile( pImpl->m_pInStream.get() ) && !SotStorage::IsOLEStorage( pImpl->m_pInStream.get() );
761 if ( !pImpl->m_pInStream->GetError() && !pImpl->bIsStorage )
762 pImpl->m_bTriedStorage = true;
765 return pImpl->bIsStorage;
769 bool SfxMedium::IsPreview_Impl() const
771 bool bPreview = false;
772 const SfxBoolItem* pPreview = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_PREVIEW, false);
773 if ( pPreview )
774 bPreview = pPreview->GetValue();
775 else
777 const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_OPTIONS, false);
778 if ( pFlags )
780 OUString aFileFlags = pFlags->GetValue();
781 aFileFlags = aFileFlags.toAsciiUpperCase();
782 if ( -1 != aFileFlags.indexOf( 'B' ) )
783 bPreview = true;
787 return bPreview;
791 void SfxMedium::StorageBackup_Impl()
793 ::ucbhelper::Content aOriginalContent;
794 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
796 bool bBasedOnOriginalFile =
797 !pImpl->pTempFile
798 && ( pImpl->m_aLogicName.isEmpty() || !pImpl->m_bSalvageMode )
799 && !GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty()
800 && GetURLObject().GetProtocol() == INetProtocol::File
801 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
803 if ( bBasedOnOriginalFile && pImpl->m_aBackupURL.isEmpty()
804 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aOriginalContent ) )
806 DoInternalBackup_Impl( aOriginalContent );
807 if( pImpl->m_aBackupURL.isEmpty() )
808 SetError(ERRCODE_SFX_CANTCREATEBACKUP);
813 OUString const & SfxMedium::GetBackup_Impl()
815 if ( pImpl->m_aBackupURL.isEmpty() )
816 StorageBackup_Impl();
818 return pImpl->m_aBackupURL;
822 uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage()
824 if ( GetError() )
825 return uno::Reference< embed::XStorage >();
827 // if the medium was constructed with a Storage: use this one, not a temp. storage
828 // if a temporary storage already exists: use it
829 if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile ) )
830 return pImpl->xStorage;
832 // if necessary close stream that was used for reading
833 if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() )
834 CloseInStream();
836 DBG_ASSERT( !pImpl->m_pOutStream, "OutStream in a readonly Medium?!" );
838 // TODO/LATER: The current solution is to store the document temporary and then copy it to the target location;
839 // in future it should be stored directly and then copied to the temporary location, since in this case no
840 // file attributes have to be preserved and system copying mechanics could be used instead of streaming.
841 CreateTempFileNoCopy();
843 return GetStorage();
847 void SfxMedium::SetEncryptionDataToStorage_Impl()
849 // in case media-descriptor contains password it should be used on opening
850 if ( !pImpl->xStorage.is() || !pImpl->m_pSet )
851 return;
853 uno::Sequence< beans::NamedValue > aEncryptionData;
854 if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
855 return;
857 // replace the password with encryption data
858 pImpl->m_pSet->ClearItem( SID_PASSWORD );
859 pImpl->m_pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) );
863 ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl->xStorage, aEncryptionData );
865 catch( const uno::Exception& )
867 SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" );
868 // TODO/LATER: set the error code in case of problem
869 // SetError(ERRCODE_IO_GENERAL);
873 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
875 // FIXME: Hmm actually lock files should be used for sftp: documents
876 // even if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT. Only the use of lock
877 // files for *local* documents is unnecessary in that case. But
878 // actually, the checks for sftp: here are just wishful thinking; I
879 // don't this there is any support for actually editing documents
880 // behind sftp: URLs anyway.
882 // Sure, there could perhaps be a 3rd-party extension that brings UCB
883 // the potential to handle files behind sftp:. But there could also be
884 // an extension that handles some arbitrary foobar: scheme *and* it
885 // could be that lock files would be the correct thing to use for
886 // foobar: documents, too. But the hardcoded test below won't know
887 // that. Clearly the knowledge whether lock files should be used or
888 // not for some URL scheme belongs in UCB, not here.
890 namespace
893 OUString tryMSOwnerFiles(const OUString& sDocURL)
895 svt::MSODocumentLockFile aMSOLockFile(sDocURL);
896 LockFileEntry aData;
899 aData = aMSOLockFile.GetLockData();
901 catch( const uno::Exception& )
903 return OUString();
906 OUString sUserData = aData[LockFileComponent::OOOUSERNAME];
908 if (!sUserData.isEmpty())
909 sUserData += " (MS Office)"; // Mention the used office suite
911 return sUserData;
914 OUString tryForeignLockfiles(const OUString& sDocURL)
916 OUString sUserData = tryMSOwnerFiles(sDocURL);
917 // here we can test for empty result, and add other known applications' lockfile testing
918 return sUserData.trim();
922 SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntry& aData,
923 bool bIsLoading, bool bOwnLock,
924 bool bHandleSysLocked)
926 ShowLockResult nResult = ShowLockResult::NoLock;
928 // tdf#92817: Simple check for empty lock file that needs to be deleted, when system locking is enabled
929 if( aData[LockFileComponent::OOOUSERNAME].isEmpty() && aData[LockFileComponent::SYSUSERNAME].isEmpty() && !bHandleSysLocked )
930 bOwnLock=true;
932 // show the interaction regarding the document opening
933 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
935 if ( xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
937 OUString aDocumentURL
938 = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset);
939 OUString aInfo;
940 ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl;
942 sal_Int32 nContinuations = 3;
944 if ( bOwnLock )
946 aInfo = aData[LockFileComponent::EDITTIME];
948 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
949 document::OwnLockOnDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo, !bIsLoading ) ) );
951 else
953 // Use a fourth continuation in case there's no filesystem lock:
954 // "Ignore lock file and open/replace the document"
955 if (!bHandleSysLocked)
956 nContinuations = 4;
958 if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
959 aInfo = aData[LockFileComponent::OOOUSERNAME];
960 else
961 aInfo = aData[LockFileComponent::SYSUSERNAME];
963 if (aInfo.isEmpty() && !GetURLObject().isAnyKnownWebDAVScheme())
964 // Try to get name of user who has locked the file using other applications
965 aInfo = tryForeignLockfiles(
966 GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
968 if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() )
969 aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )";
971 if (!bIsLoading) // so, !bHandleSysLocked
973 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny(
974 document::LockedOnSavingRequest(OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo)));
975 // Currently, only the last "Retry" continuation (meaning ignore the lock and try overwriting) can be returned.
977 else /*logically therefore bIsLoading is set */
979 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
980 document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) );
984 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations);
985 aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
986 aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
987 aContinuations[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl.get() );
988 if (nContinuations > 3)
990 // We use InteractionRetry to reflect that user wants to
991 // ignore the (stale?) alien lock file and open/overwrite the document
992 aContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get());
994 xInteractionRequestImpl->setContinuations( aContinuations );
996 xHandler->handle( xInteractionRequestImpl.get() );
998 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
999 if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
1001 SetError(ERRCODE_ABORT);
1003 else if ( uno::Reference< task::XInteractionDisapprove >( xSelected.get(), uno::UNO_QUERY ).is() )
1005 // own lock on loading, user has selected to ignore the lock
1006 // own lock on saving, user has selected to ignore the lock
1007 // alien lock on loading, user has selected to edit a copy of document
1008 // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location
1009 if ( !bOwnLock ) // bIsLoading implied from outermost condition
1011 // means that a copy of the document should be opened
1012 GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) );
1014 else
1015 nResult = ShowLockResult::Succeeded;
1017 else if (uno::Reference< task::XInteractionRetry >(xSelected.get(), uno::UNO_QUERY).is())
1019 // User decided to ignore the alien (stale?) lock file without filesystem lock
1020 nResult = ShowLockResult::Succeeded;
1022 else // if ( XSelected == aContinuations[1] )
1024 // own lock on loading, user has selected to open readonly
1025 // own lock on saving, user has selected to open readonly
1026 // alien lock on loading, user has selected to retry saving
1027 // TODO/LATER: alien lock on saving, user has selected to retry saving
1029 if ( bIsLoading )
1030 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1031 else
1032 nResult = ShowLockResult::Try;
1035 else
1037 if ( bIsLoading )
1039 // if no interaction handler is provided the default answer is open readonly
1040 // that usually happens in case the document is loaded per API
1041 // so the document must be opened readonly for backward compatibility
1042 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1044 else
1045 SetError(ERRCODE_IO_ACCESSDENIED);
1049 return nResult;
1053 bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg)
1055 // system file locking is not active, ask user whether he wants to open the document without any locking
1056 uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
1058 if (xHandler.is())
1060 ::rtl::Reference< ::ucbhelper::InteractionRequest > xIgnoreRequestImpl;
1062 switch (nWhichDlg)
1064 case MessageDlg::LockFileIgnore:
1065 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileIgnoreRequest() ));
1066 break;
1067 case MessageDlg::LockFileCorrupt:
1068 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileCorruptRequest() ));
1069 break;
1072 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(2);
1073 aContinuations[0] = new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl.get());
1074 aContinuations[1] = new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl.get());
1075 xIgnoreRequestImpl->setContinuations(aContinuations);
1077 xHandler->handle(xIgnoreRequestImpl.get());
1079 ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection();
1080 bool bReadOnly = uno::Reference< task::XInteractionApprove >(xSelected.get(), uno::UNO_QUERY).is();
1082 if (bReadOnly)
1084 GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
1086 else
1088 SetError(ERRCODE_ABORT);
1091 return bReadOnly;
1094 return false;
1097 namespace
1099 bool isSuitableProtocolForLocking(const OUString & rLogicName)
1101 INetURLObject aUrl( rLogicName );
1102 INetProtocol eProt = aUrl.GetProtocol();
1103 #if !HAVE_FEATURE_MACOSX_SANDBOX
1104 if (eProt == INetProtocol::File) {
1105 return true;
1107 #endif
1108 return eProt == INetProtocol::Smb || eProt == INetProtocol::Sftp;
1112 #endif // HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1114 // sets SID_DOC_READONLY if the document cannot be opened for editing
1115 // if user cancel the loading the ERROR_ABORT is set
1116 SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bNoUI,
1117 bool bTryIgnoreLockFile,
1118 LockFileEntry* pLockData)
1120 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1121 (void) bLoading;
1122 (void) bNoUI;
1123 (void) bTryIgnoreLockFile;
1124 (void) pLockData;
1125 return LockFileResult::Succeeded;
1126 #else
1127 LockFileResult eResult = LockFileResult::Failed;
1129 // check if path scheme is http:// or https://
1130 // may be this is better if used always, in Android and iOS as well?
1131 // if this code should be always there, remember to move the relevant code in UnlockFile method as well !
1133 if ( GetURLObject().isAnyKnownWebDAVScheme() )
1135 // do nothing if WebDAV locking is disabled
1136 if (!IsWebDAVLockingUsed())
1137 return LockFileResult::Succeeded;
1141 bool bResult = pImpl->m_bLocked;
1142 bool bIsTemplate = false;
1143 // so, this is webdav stuff...
1144 if ( !bResult )
1146 // no read-write access is necessary on loading if the document is explicitly opened as copy
1147 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
1148 bIsTemplate = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
1151 if ( !bIsTemplate && !bResult && !IsReadOnly() )
1153 ShowLockResult bUIStatus = ShowLockResult::NoLock;
1156 if( !bResult )
1158 uno::Reference< task::XInteractionHandler > xCHandler = GetInteractionHandler( true );
1159 Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment(
1160 xCHandler, Reference< css::ucb::XProgressHandler >() );
1162 ucbhelper::Content aContentToLock(
1163 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1164 xComEnv, comphelper::getProcessComponentContext() );
1168 aContentToLock.lock();
1169 bResult = true;
1171 catch ( ucb::InteractiveLockingLockedException& )
1173 // received when the resource is already locked
1174 if (!bNoUI || pLockData)
1176 // get the lock owner, using a special ucb.webdav property
1177 // the owner property retrieved here is what the other principal send the server
1178 // when activating the lock.
1179 // See http://tools.ietf.org/html/rfc4918#section-14.17 for details
1180 LockFileEntry aLockData;
1181 aLockData[LockFileComponent::OOOUSERNAME] = "Unknown user";
1182 // This solution works right when the LO user name and the WebDAV user
1183 // name are the same.
1184 // A better thing to do would be to obtain the 'real' WebDAV user name,
1185 // but that's not possible from a WebDAV UCP provider client.
1186 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
1187 // use the current LO user name as the system name
1188 aLockData[LockFileComponent::SYSUSERNAME]
1189 = aOwnData[LockFileComponent::SYSUSERNAME];
1191 uno::Sequence<css::ucb::Lock> aLocks;
1192 // getting the property, send a PROPFIND to the server over the net
1193 if ((aContentToLock.getPropertyValue("DAV:lockdiscovery") >>= aLocks) && aLocks.hasElements())
1195 // got at least a lock, show the owner of the first lock returned
1196 css::ucb::Lock aLock = aLocks[0];
1197 OUString aOwner;
1198 if (aLock.Owner >>= aOwner)
1200 // we need to display the WebDAV user name owning the lock, not the local one
1201 aLockData[LockFileComponent::OOOUSERNAME] = aOwner;
1205 if (!bNoUI)
1207 bUIStatus = ShowLockedDocumentDialog(aLockData, bLoading, false,
1208 true);
1211 if (pLockData)
1213 std::copy(aLockData.begin(), aLockData.end(), pLockData->begin());
1217 catch( ucb::InteractiveNetworkWriteException& )
1219 // This catch it's not really needed, here just for the sake of documentation on the behaviour.
1220 // This is the most likely reason:
1221 // - the remote site is a WebDAV with special configuration: read/only for read operations
1222 // and read/write for write operations, the user is not allowed to lock/write and
1223 // she cancelled the credentials request.
1224 // this is not actually an error, but the exception is sent directly from ucb, avoiding the automatic
1225 // management that takes part in cancelCommandExecution()
1226 // Unfortunately there is no InteractiveNetwork*Exception available to signal this more correctly
1227 // since it mostly happens on read/only part of webdav, this can be the most correct
1228 // exception available
1230 catch( uno::Exception& )
1233 } while( !bResult && bUIStatus == ShowLockResult::Try );
1236 pImpl->m_bLocked = bResult;
1238 if ( !bResult && GetError() == ERRCODE_NONE )
1240 // the error should be set in case it is storing process
1241 // or the document has been opened for editing explicitly
1242 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
1244 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
1245 SetError(ERRCODE_IO_ACCESSDENIED);
1246 else
1247 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1250 // when the file is locked, get the current file date
1251 if ( bResult && DocNeedsFileDateCheck() )
1252 GetInitFileDate( true );
1254 if ( bResult )
1255 eResult = LockFileResult::Succeeded;
1257 catch ( const uno::Exception& )
1259 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
1261 return eResult;
1264 if (!IsLockingUsed())
1265 return LockFileResult::Succeeded;
1266 if (GetURLObject().HasError())
1267 return eResult;
1271 if ( pImpl->m_bLocked && bLoading
1272 && GetURLObject().GetProtocol() == INetProtocol::File )
1274 // if the document is already locked the system locking might be temporarily off after storing
1275 // check whether the system file locking should be taken again
1276 GetLockingStream_Impl();
1279 bool bResult = pImpl->m_bLocked;
1281 if ( !bResult )
1283 // no read-write access is necessary on loading if the document is explicitly opened as copy
1284 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
1285 bResult = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
1288 if ( !bResult && !IsReadOnly() )
1290 bool bContentReadonly = false;
1291 if ( bLoading && GetURLObject().GetProtocol() == INetProtocol::File )
1293 // let the original document be opened to check the possibility to open it for editing
1294 // and to let the writable stream stay open to hold the lock on the document
1295 GetLockingStream_Impl();
1298 // "IsReadOnly" property does not allow to detect whether the file is readonly always
1299 // so we try always to open the file for editing
1300 // the file is readonly only in case the read-write stream can not be opened
1301 if ( bLoading && !pImpl->m_xLockingStream.is() )
1305 // MediaDescriptor does this check also, the duplication should be avoided in future
1306 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1307 ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
1308 aContent.getPropertyValue("IsReadOnly") >>= bContentReadonly;
1310 catch( const uno::Exception& ) {}
1313 // do further checks only if the file not readonly in fs
1314 if ( !bContentReadonly )
1316 // the special file locking should be used only for suitable URLs
1317 if ( isSuitableProtocolForLocking( pImpl->m_aLogicName ) )
1320 // in case of storing the document should request the output before locking
1321 if ( bLoading )
1323 // let the stream be opened to check the system file locking
1324 GetMedium_Impl();
1325 if (GetError() != ERRCODE_NONE) {
1326 return eResult;
1330 ShowLockResult bUIStatus = ShowLockResult::NoLock;
1332 // check whether system file locking has been used, the default value is false
1333 bool bUseSystemLock = comphelper::isFileUrl( pImpl->m_aLogicName ) && IsSystemFileLockingUsed();
1335 // TODO/LATER: This implementation does not allow to detect the system lock on saving here, actually this is no big problem
1336 // if system lock is used the writeable stream should be available
1337 bool bHandleSysLocked = ( bLoading && bUseSystemLock && !pImpl->xStream.is() && !pImpl->m_pOutStream );
1339 // The file is attempted to get locked for the duration of lockfile creation on save
1340 std::unique_ptr<osl::File> pFileLock;
1341 if (!bLoading && bUseSystemLock && pImpl->pTempFile)
1343 INetURLObject aDest(GetURLObject());
1344 OUString aDestURL(aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1346 if (comphelper::isFileUrl(aDestURL) || !aDest.removeSegment())
1348 pFileLock = std::make_unique<osl::File>(aDestURL);
1349 auto rc = pFileLock->open(osl_File_OpenFlag_Write);
1350 if (rc == osl::FileBase::E_ACCES)
1351 bHandleSysLocked = true;
1359 ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
1361 std::unique_ptr<svt::MSODocumentLockFile> pMSOLockFile;
1362 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
1363 if (rOpt.IsMSOLockFileCreationIsEnabled() && svt::MSODocumentLockFile::IsMSOSupportedFileFormat(pImpl->m_aLogicName))
1365 pMSOLockFile.reset(new svt::MSODocumentLockFile(pImpl->m_aLogicName));
1366 pImpl->m_bMSOLockFileCreated = true;
1369 bool bIoErr = false;
1371 if (!bHandleSysLocked)
1375 bResult = aLockFile.CreateOwnLockFile();
1376 if(pMSOLockFile)
1377 bResult &= pMSOLockFile->CreateOwnLockFile();
1379 catch (const uno::Exception&)
1381 if (tools::IsMappedWebDAVPath(GetURLObject().GetMainURL(
1382 INetURLObject::DecodeMechanism::NONE)))
1384 // This is a path that redirects to a WebDAV resource;
1385 // so failure creating lockfile is not an error here.
1386 bResult = true;
1388 else if (bLoading && !bNoUI)
1390 bIoErr = true;
1391 ShowLockFileProblemDialog(MessageDlg::LockFileIgnore);
1392 bResult = true; // always delete the defect lock-file
1396 // in case OOo locking is turned off the lock file is still written if possible
1397 // but it is ignored while deciding whether the document should be opened for editing or not
1398 if (!bResult && !IsOOoLockFileUsed() && !bIoErr)
1400 bResult = true;
1401 // take the ownership over the lock file
1402 aLockFile.OverwriteOwnLockFile();
1404 if(pMSOLockFile)
1405 pMSOLockFile->OverwriteOwnLockFile();
1409 if ( !bResult )
1411 LockFileEntry aData;
1414 aData = aLockFile.GetLockData();
1416 catch (const io::WrongFormatException&)
1418 // we get empty or corrupt data
1419 // info to the user
1420 if (!bIoErr && bLoading && !bNoUI )
1421 bResult = ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt);
1423 // not show the Lock Document Dialog
1424 bIoErr = true;
1426 catch( const uno::Exception& )
1428 // show the Lock Document Dialog, when locked from other app
1429 bIoErr = !bHandleSysLocked;
1432 bool bOwnLock = false;
1434 if (!bHandleSysLocked)
1436 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
1437 bOwnLock = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME];
1439 if (bOwnLock
1440 && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST]
1441 && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL])
1443 // this is own lock from the same installation, it could remain because of crash
1444 bResult = true;
1448 if ( !bResult && !bIoErr)
1450 if (!bNoUI)
1451 bUIStatus = ShowLockedDocumentDialog(
1452 aData, bLoading, bOwnLock, bHandleSysLocked);
1453 else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked)
1454 bUIStatus = ShowLockResult::Succeeded;
1456 if ( bUIStatus == ShowLockResult::Succeeded )
1458 // take the ownership over the lock file
1459 bResult = aLockFile.OverwriteOwnLockFile();
1461 if(pMSOLockFile)
1462 pMSOLockFile->OverwriteOwnLockFile();
1464 else if (bLoading && !bHandleSysLocked)
1465 eResult = LockFileResult::FailedLockFile;
1467 if (!bResult && pLockData)
1469 std::copy(aData.begin(), aData.end(), pLockData->begin());
1474 catch( const uno::Exception& )
1477 } while( !bResult && bUIStatus == ShowLockResult::Try );
1479 pImpl->m_bLocked = bResult;
1481 else
1483 // this is no file URL, check whether the file is readonly
1484 bResult = !bContentReadonly;
1489 if ( !bResult && GetError() == ERRCODE_NONE )
1491 // the error should be set in case it is storing process
1492 // or the document has been opened for editing explicitly
1493 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
1495 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
1496 SetError(ERRCODE_IO_ACCESSDENIED);
1497 else
1498 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1501 // when the file is locked, get the current file date
1502 if ( bResult && DocNeedsFileDateCheck() )
1503 GetInitFileDate( true );
1505 if ( bResult )
1506 eResult = LockFileResult::Succeeded;
1508 catch( const uno::Exception& )
1510 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: high probability, that the content has not been created" );
1513 return eResult;
1514 #endif
1518 uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
1520 if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage )
1521 return pImpl->xStorage;
1523 uno::Sequence< uno::Any > aArgs( 2 );
1525 // the medium should be retrieved before temporary file creation
1526 // to let the MediaDescriptor be filled with the streams
1527 GetMedium_Impl();
1529 if ( bCreateTempFile )
1530 CreateTempFile( false );
1532 GetMedium_Impl();
1534 if ( GetError() )
1535 return pImpl->xStorage;
1537 const SfxBoolItem* pRepairItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_REPAIRPACKAGE, false);
1538 if ( pRepairItem && pRepairItem->GetValue() )
1540 // the storage should be created for repairing mode
1541 CreateTempFile( false );
1542 GetMedium_Impl();
1544 Reference< css::ucb::XProgressHandler > xProgressHandler;
1545 Reference< css::task::XStatusIndicator > xStatusIndicator;
1547 const SfxUnoAnyItem* pxProgressItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetItemSet(), SID_PROGRESS_STATUSBAR_CONTROL, false);
1548 if( pxProgressItem && ( pxProgressItem->GetValue() >>= xStatusIndicator ) )
1549 xProgressHandler.set( new utl::ProgressHandlerWrap( xStatusIndicator ) );
1551 uno::Sequence< beans::PropertyValue > aAddProps( 2 );
1552 aAddProps[0].Name = "RepairPackage";
1553 aAddProps[0].Value <<= true;
1554 aAddProps[1].Name = "StatusIndicator";
1555 aAddProps[1].Value <<= xProgressHandler;
1557 // the first arguments will be filled later
1558 aArgs.realloc( 3 );
1559 aArgs[2] <<= aAddProps;
1562 if ( pImpl->xStream.is() )
1564 // since the storage is based on temporary stream we open it always read-write
1565 aArgs[0] <<= pImpl->xStream;
1566 aArgs[1] <<= embed::ElementModes::READWRITE;
1567 pImpl->bStorageBasedOnInStream = true;
1568 if (pImpl->m_bDisableFileSync)
1570 // Forward NoFileSync to the storage factory.
1571 aArgs.realloc(3);
1572 uno::Sequence<beans::PropertyValue> aProperties(
1573 comphelper::InitPropertySequence({ { "NoFileSync", uno::makeAny(true) } }));
1574 aArgs[2] <<= aProperties;
1577 else if ( pImpl->xInputStream.is() )
1579 // since the storage is based on temporary stream we open it always read-write
1580 aArgs[0] <<= pImpl->xInputStream;
1581 aArgs[1] <<= embed::ElementModes::READ;
1582 pImpl->bStorageBasedOnInStream = true;
1584 else
1586 CloseStreams_Impl();
1587 aArgs[0] <<= pImpl->m_aName;
1588 aArgs[1] <<= embed::ElementModes::READ;
1589 pImpl->bStorageBasedOnInStream = false;
1594 pImpl->xStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ),
1595 uno::UNO_QUERY );
1597 catch( const uno::Exception& )
1599 // impossibility to create the storage is no error
1602 if( ( pImpl->nLastStorageError = GetError() ) != ERRCODE_NONE )
1604 pImpl->xStorage = nullptr;
1605 if ( pImpl->m_pInStream )
1606 pImpl->m_pInStream->Seek(0);
1607 return uno::Reference< embed::XStorage >();
1610 pImpl->m_bTriedStorage = true;
1612 // TODO/LATER: Get versionlist on demand
1613 if ( pImpl->xStorage.is() )
1615 SetEncryptionDataToStorage_Impl();
1616 GetVersionList();
1619 const SfxInt16Item* pVersion = SfxItemSet::GetItem<SfxInt16Item>(pImpl->m_pSet.get(), SID_VERSION, false);
1621 bool bResetStorage = false;
1622 if ( pVersion && pVersion->GetValue() )
1624 // Read all available versions
1625 if ( pImpl->aVersions.hasElements() )
1627 // Search for the version fits the comment
1628 // The versions are numbered starting with 1, versions with
1629 // negative versions numbers are counted backwards from the
1630 // current version
1631 short nVersion = pVersion->GetValue();
1632 if ( nVersion<0 )
1633 nVersion = static_cast<short>(pImpl->aVersions.getLength()) + nVersion;
1634 else // nVersion > 0; pVersion->GetValue() != 0 was the condition to this block
1635 nVersion--;
1637 util::RevisionTag& rTag = pImpl->aVersions[nVersion];
1639 // Open SubStorage for all versions
1640 uno::Reference < embed::XStorage > xSub = pImpl->xStorage->openStorageElement( "Versions",
1641 embed::ElementModes::READ );
1643 DBG_ASSERT( xSub.is(), "Version list, but no Versions!" );
1645 // There the version is stored as packed Stream
1646 uno::Reference < io::XStream > xStr = xSub->openStreamElement( rTag.Identifier, embed::ElementModes::READ );
1647 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( xStr ));
1648 if ( pStream && pStream->GetError() == ERRCODE_NONE )
1650 // Unpack Stream in TempDir
1651 ::utl::TempFile aTempFile;
1652 const OUString& aTmpName = aTempFile.GetURL();
1653 SvFileStream aTmpStream( aTmpName, SFX_STREAM_READWRITE );
1655 pStream->ReadStream( aTmpStream );
1656 pStream.reset();
1657 aTmpStream.Close();
1659 // Open data as Storage
1660 pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
1661 pImpl->xStorage = comphelper::OStorageHelper::GetStorageFromURL( aTmpName, embed::ElementModes::READ );
1662 pImpl->bStorageBasedOnInStream = false;
1663 OUString aTemp;
1664 osl::FileBase::getSystemPathFromFileURL( aTmpName, aTemp );
1665 SetPhysicalName_Impl( aTemp );
1667 pImpl->bIsTemp = true;
1668 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1669 // TODO/MBA
1670 pImpl->aVersions.realloc(0);
1672 else
1673 bResetStorage = true;
1676 else
1677 bResetStorage = true;
1680 if ( bResetStorage )
1682 pImpl->xStorage.clear();
1683 if ( pImpl->m_pInStream )
1684 pImpl->m_pInStream->Seek( 0 );
1687 pImpl->bIsStorage = pImpl->xStorage.is();
1688 return pImpl->xStorage;
1692 uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly )
1694 if ( !GetError() && !pImpl->m_xZipStorage.is() )
1696 GetMedium_Impl();
1700 // we can not sign document if there is no stream
1701 // should it be possible at all?
1702 if ( !bReadOnly && pImpl->xStream.is() )
1704 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
1706 else if ( pImpl->xInputStream.is() )
1708 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream );
1711 catch( const uno::Exception& )
1713 SAL_WARN( "sfx.doc", "No possibility to get readonly version of storage from medium!" );
1716 if ( GetError() ) // do not remove warnings
1717 ResetError();
1720 return pImpl->m_xZipStorage;
1724 void SfxMedium::CloseZipStorage_Impl()
1726 if ( pImpl->m_xZipStorage.is() )
1728 try {
1729 pImpl->m_xZipStorage->dispose();
1730 } catch( const uno::Exception& )
1733 pImpl->m_xZipStorage.clear();
1737 void SfxMedium::CloseStorage()
1739 if ( pImpl->xStorage.is() )
1741 uno::Reference < lang::XComponent > xComp = pImpl->xStorage;
1742 // in the salvage mode the medium does not own the storage
1743 if ( pImpl->bDisposeStorage && !pImpl->m_bSalvageMode )
1745 try {
1746 xComp->dispose();
1747 } catch( const uno::Exception& )
1749 SAL_WARN( "sfx.doc", "Medium's storage is already disposed!" );
1753 pImpl->xStorage.clear();
1754 pImpl->bStorageBasedOnInStream = false;
1757 pImpl->m_bTriedStorage = false;
1758 pImpl->bIsStorage = false;
1761 void SfxMedium::CanDisposeStorage_Impl( bool bDisposeStorage )
1763 pImpl->bDisposeStorage = bDisposeStorage;
1766 bool SfxMedium::WillDisposeStorageOnClose_Impl()
1768 return pImpl->bDisposeStorage;
1771 StreamMode SfxMedium::GetOpenMode() const
1773 return pImpl->m_nStorOpenMode;
1776 void SfxMedium::SetOpenMode( StreamMode nStorOpen,
1777 bool bDontClose )
1779 if ( pImpl->m_nStorOpenMode != nStorOpen )
1781 pImpl->m_nStorOpenMode = nStorOpen;
1783 if( !bDontClose )
1785 if ( pImpl->xStorage.is() )
1786 CloseStorage();
1788 CloseStreams_Impl();
1794 bool SfxMedium::UseBackupToRestore_Impl( ::ucbhelper::Content& aOriginalContent,
1795 const Reference< css::ucb::XCommandEnvironment >& xComEnv )
1799 ::ucbhelper::Content aTransactCont( pImpl->m_aBackupURL, xComEnv, comphelper::getProcessComponentContext() );
1801 Reference< XInputStream > aOrigInput = aTransactCont.openStream();
1802 aOriginalContent.writeStream( aOrigInput, true );
1803 return true;
1805 catch( const Exception& )
1807 // in case of failure here the backup file should not be removed
1808 // TODO/LATER: a message should be used to let user know about the backup
1809 pImpl->m_bRemoveBackup = false;
1810 // TODO/LATER: needs a specific error code
1811 pImpl->m_eError = ERRCODE_IO_GENERAL;
1814 return false;
1818 bool SfxMedium::StorageCommit_Impl()
1820 bool bResult = false;
1821 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1822 ::ucbhelper::Content aOriginalContent;
1824 if ( pImpl->xStorage.is() )
1826 if ( !GetError() )
1828 uno::Reference < embed::XTransactedObject > xTrans( pImpl->xStorage, uno::UNO_QUERY );
1829 if ( xTrans.is() )
1833 xTrans->commit();
1834 CloseZipStorage_Impl();
1835 bResult = true;
1837 catch ( const embed::UseBackupException& aBackupExc )
1839 // since the temporary file is created always now, the scenario is close to be impossible
1840 if ( !pImpl->pTempFile )
1842 OSL_ENSURE( !pImpl->m_aBackupURL.isEmpty(), "No backup on storage commit!" );
1843 if ( !pImpl->m_aBackupURL.isEmpty()
1844 && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1845 xDummyEnv, comphelper::getProcessComponentContext(),
1846 aOriginalContent ) )
1848 // use backup to restore the file
1849 // the storage has already disconnected from original location
1850 CloseAndReleaseStreams_Impl();
1851 if ( !UseBackupToRestore_Impl( aOriginalContent, xDummyEnv ) )
1853 // connect the medium to the temporary file of the storage
1854 pImpl->aContent = ::ucbhelper::Content();
1855 pImpl->m_aName = aBackupExc.TemporaryFileURL;
1856 OSL_ENSURE( !pImpl->m_aName.isEmpty(), "The exception _must_ contain the temporary URL!" );
1861 if (!GetError())
1862 SetError(ERRCODE_IO_GENERAL);
1864 catch ( const uno::Exception& )
1866 //TODO/LATER: improve error handling
1867 SetError(ERRCODE_IO_GENERAL);
1873 return bResult;
1877 void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject& aSource,
1878 const INetURLObject& aDest,
1879 const Reference< css::ucb::XCommandEnvironment >& xComEnv )
1881 Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1882 ::ucbhelper::Content aOriginalContent;
1886 aOriginalContent = ::ucbhelper::Content( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
1888 catch ( const css::ucb::CommandAbortedException& )
1890 pImpl->m_eError = ERRCODE_ABORT;
1892 catch ( const css::ucb::CommandFailedException& )
1894 pImpl->m_eError = ERRCODE_ABORT;
1896 catch (const css::ucb::ContentCreationException& ex)
1898 pImpl->m_eError = ERRCODE_IO_GENERAL;
1899 if (
1900 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) ||
1901 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
1904 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
1907 catch (const css::uno::Exception&)
1909 pImpl->m_eError = ERRCODE_IO_GENERAL;
1912 if( pImpl->m_eError && !pImpl->m_eError.IsWarning() )
1913 return;
1915 if ( pImpl->xStorage.is() )
1916 CloseStorage();
1918 CloseStreams_Impl();
1920 ::ucbhelper::Content aTempCont;
1921 if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aTempCont ) )
1923 bool bTransactStarted = false;
1924 const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
1925 bool bOverWrite = !pOverWrite || pOverWrite->GetValue();
1926 bool bResult = false;
1930 OUString aSourceMainURL = aSource.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1931 OUString aDestMainURL = aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1933 sal_uInt64 nAttributes = GetDefaultFileAttributes(aDestMainURL);
1934 if (IsFileMovable(aDest)
1935 && osl::File::replace(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None)
1937 if (nAttributes)
1938 // Adjust attributes, source might be created with
1939 // the osl_File_OpenFlag_Private flag.
1940 osl::File::setAttributes(aDestMainURL, nAttributes);
1941 bResult = true;
1943 else
1945 if (bOverWrite && ::utl::UCBContentHelper::IsDocument(aDestMainURL))
1947 if( pImpl->m_aBackupURL.isEmpty() )
1948 DoInternalBackup_Impl( aOriginalContent );
1950 if( !pImpl->m_aBackupURL.isEmpty() )
1952 Reference< XInputStream > aTempInput = aTempCont.openStream();
1953 bTransactStarted = true;
1954 aOriginalContent.setPropertyValue( "Size", uno::makeAny( sal_Int64(0) ) );
1955 aOriginalContent.writeStream( aTempInput, bOverWrite );
1956 bResult = true;
1958 else
1960 pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
1963 else
1965 Reference< XInputStream > aTempInput = aTempCont.openStream();
1966 aOriginalContent.writeStream( aTempInput, bOverWrite );
1967 bResult = true;
1971 catch ( const css::ucb::CommandAbortedException& )
1973 pImpl->m_eError = ERRCODE_ABORT;
1975 catch ( const css::ucb::CommandFailedException& )
1977 pImpl->m_eError = ERRCODE_ABORT;
1979 catch ( const css::ucb::InteractiveIOException& r )
1981 if ( r.Code == IOErrorCode_ACCESS_DENIED )
1982 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
1983 else if ( r.Code == IOErrorCode_NOT_EXISTING )
1984 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
1985 else if ( r.Code == IOErrorCode_CANT_READ )
1986 pImpl->m_eError = ERRCODE_IO_CANTREAD;
1987 else
1988 pImpl->m_eError = ERRCODE_IO_GENERAL;
1990 catch ( const css::uno::Exception& )
1992 pImpl->m_eError = ERRCODE_IO_GENERAL;
1995 if ( bResult )
1997 if ( pImpl->pTempFile )
1999 pImpl->pTempFile->EnableKillingFile();
2000 pImpl->pTempFile.reset();
2003 else if ( bTransactStarted )
2005 UseBackupToRestore_Impl( aOriginalContent, xDummyEnv );
2008 else
2009 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2013 bool SfxMedium::TryDirectTransfer( const OUString& aURL, SfxItemSet const & aTargetSet )
2015 if ( GetError() )
2016 return false;
2018 // if the document had no password it should be stored without password
2019 // if the document had password it should be stored with the same password
2020 // otherwise the stream copying can not be done
2021 const SfxStringItem* pNewPassItem = aTargetSet.GetItem<SfxStringItem>(SID_PASSWORD, false);
2022 const SfxStringItem* pOldPassItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_PASSWORD, false);
2023 if ( ( !pNewPassItem && !pOldPassItem )
2024 || ( pNewPassItem && pOldPassItem && pNewPassItem->GetValue() == pOldPassItem->GetValue() ) )
2026 // the filter must be the same
2027 const SfxStringItem* pNewFilterItem = aTargetSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
2028 const SfxStringItem* pOldFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_FILTER_NAME, false);
2029 if ( pNewFilterItem && pOldFilterItem && pNewFilterItem->GetValue() == pOldFilterItem->GetValue() )
2031 // get the input stream and copy it
2032 // in case of success return true
2033 uno::Reference< io::XInputStream > xInStream = GetInputStream();
2035 ResetError();
2036 if ( xInStream.is() )
2040 uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
2041 sal_Int64 nPos = 0;
2042 if ( xSeek.is() )
2044 nPos = xSeek->getPosition();
2045 xSeek->seek( 0 );
2048 uno::Reference < css::ucb::XCommandEnvironment > xEnv;
2049 ::ucbhelper::Content aTargetContent( aURL, xEnv, comphelper::getProcessComponentContext() );
2051 InsertCommandArgument aInsertArg;
2052 aInsertArg.Data = xInStream;
2053 const SfxBoolItem* pOverWrite = aTargetSet.GetItem<SfxBoolItem>(SID_OVERWRITE, false);
2054 if ( pOverWrite && !pOverWrite->GetValue() ) // argument says: never overwrite
2055 aInsertArg.ReplaceExisting = false;
2056 else
2057 aInsertArg.ReplaceExisting = true; // default is overwrite existing files
2059 Any aCmdArg;
2060 aCmdArg <<= aInsertArg;
2061 aTargetContent.executeCommand( "insert",
2062 aCmdArg );
2064 if ( xSeek.is() )
2065 xSeek->seek( nPos );
2067 return true;
2069 catch( const uno::Exception& )
2075 return false;
2079 void SfxMedium::Transfer_Impl()
2081 // The transfer is required only in two cases: either if there is a temporary file or if there is a salvage item
2082 OUString aNameURL;
2083 if ( pImpl->pTempFile )
2084 aNameURL = pImpl->pTempFile->GetURL();
2085 else if ( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
2087 // makes sense only in case logic name is set
2088 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aNameURL )
2089 != osl::FileBase::E_None )
2090 SAL_WARN( "sfx.doc", "The medium name is not convertible!" );
2093 if ( aNameURL.isEmpty() || ( pImpl->m_eError && !pImpl->m_eError.IsWarning() ) )
2094 return;
2096 SAL_INFO( "sfx.doc", "SfxMedium::Transfer_Impl, copying to target" );
2098 Reference < css::ucb::XCommandEnvironment > xEnv;
2099 Reference< XOutputStream > rOutStream;
2101 // in case an output stream is provided from outside and the URL is correct
2102 // commit to the stream
2103 if (pImpl->m_aLogicName.startsWith("private:stream"))
2105 // TODO/LATER: support storing to SID_STREAM
2106 const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
2107 if( pOutStreamItem && ( pOutStreamItem->GetValue() >>= rOutStream ) )
2109 if ( pImpl->xStorage.is() )
2110 CloseStorage();
2112 CloseStreams_Impl();
2114 INetURLObject aSource( aNameURL );
2115 ::ucbhelper::Content aTempCont;
2116 if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aTempCont ) )
2120 sal_Int32 nRead;
2121 sal_Int32 nBufferSize = 32767;
2122 Sequence < sal_Int8 > aSequence ( nBufferSize );
2123 Reference< XInputStream > aTempInput = aTempCont.openStream();
2127 nRead = aTempInput->readBytes ( aSequence, nBufferSize );
2128 if ( nRead < nBufferSize )
2130 Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
2131 rOutStream->writeBytes ( aTempBuf );
2133 else
2134 rOutStream->writeBytes ( aSequence );
2136 while ( nRead == nBufferSize );
2138 // remove temporary file
2139 if ( pImpl->pTempFile )
2141 pImpl->pTempFile->EnableKillingFile();
2142 pImpl->pTempFile.reset();
2145 catch( const Exception& )
2149 else
2151 SAL_WARN( "sfx.doc", "Illegal Output stream parameter!" );
2152 SetError(ERRCODE_IO_GENERAL);
2155 // free the reference
2156 if ( pImpl->m_pSet )
2157 pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
2159 return;
2162 GetContent();
2163 if ( !pImpl->aContent.get().is() )
2165 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2166 return;
2169 INetURLObject aDest( GetURLObject() );
2171 // source is the temp file written so far
2172 INetURLObject aSource( aNameURL );
2174 // a special case, an interaction handler should be used for
2175 // authentication in case it is available
2176 Reference< css::ucb::XCommandEnvironment > xComEnv;
2177 Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
2178 if (xInteractionHandler.is())
2179 xComEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler,
2180 Reference< css::ucb::XProgressHandler >() );
2182 OUString aDestURL( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2184 if ( comphelper::isFileUrl( aDestURL ) || !aDest.removeSegment() )
2186 TransactedTransferForFS_Impl( aSource, aDest, xComEnv );
2188 if (!pImpl->m_bDisableFileSync)
2190 // Hideous - no clean way to do this, so we re-open the file just to fsync it
2191 osl::File aFile( aDestURL );
2192 if ( aFile.open( osl_File_OpenFlag_Write ) == osl::FileBase::E_None )
2194 aFile.sync();
2195 SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL << "'" );
2196 aFile.close();
2200 else
2202 // create content for the parent folder and call transfer on that content with the source content
2203 // and the destination file name as parameters
2204 ::ucbhelper::Content aSourceContent;
2205 ::ucbhelper::Content aTransferContent;
2207 ::ucbhelper::Content aDestContent;
2208 (void)::ucbhelper::Content::create( aDestURL, xComEnv, comphelper::getProcessComponentContext(), aDestContent );
2209 // For checkin, we need the object URL, not the parent folder:
2210 if ( !IsInCheckIn( ) )
2212 // Get the parent URL from the XChild if possible: why would the URL necessarily have
2213 // a hierarchical path? It's not always the case for CMIS.
2214 Reference< css::container::XChild> xChild( aDestContent.get(), uno::UNO_QUERY );
2215 OUString sParentUrl;
2216 if ( xChild.is( ) )
2218 Reference< css::ucb::XContent > xParent( xChild->getParent( ), uno::UNO_QUERY );
2219 if ( xParent.is( ) )
2221 sParentUrl = xParent->getIdentifier( )->getContentIdentifier();
2225 if ( sParentUrl.isEmpty() )
2226 aDestURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2227 // adjust to above aDest.removeSegment()
2228 else
2229 aDestURL = sParentUrl;
2232 // LongName wasn't defined anywhere, only used here... get the Title instead
2233 // as it's less probably empty
2234 OUString aFileName;
2235 Any aAny = aDestContent.getPropertyValue("Title");
2236 aAny >>= aFileName;
2237 aAny = aDestContent.getPropertyValue( "ObjectId" );
2238 OUString sObjectId;
2239 aAny >>= sObjectId;
2240 if ( aFileName.isEmpty() )
2241 aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2245 aTransferContent = ::ucbhelper::Content( aDestURL, xComEnv, comphelper::getProcessComponentContext() );
2247 catch (const css::ucb::ContentCreationException& ex)
2249 pImpl->m_eError = ERRCODE_IO_GENERAL;
2250 if (
2251 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER ) ||
2252 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
2255 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
2258 catch (const css::uno::Exception&)
2260 pImpl->m_eError = ERRCODE_IO_GENERAL;
2263 if ( !pImpl->m_eError || pImpl->m_eError.IsWarning() )
2265 // free resources, otherwise the transfer may fail
2266 if ( pImpl->xStorage.is() )
2267 CloseStorage();
2269 CloseStreams_Impl();
2271 (void)::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent );
2273 // check for external parameters that may customize the handling of NameClash situations
2274 const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
2275 sal_Int32 nNameClash;
2276 if ( pOverWrite && !pOverWrite->GetValue() )
2277 // argument says: never overwrite
2278 nNameClash = NameClash::ERROR;
2279 else
2280 // default is overwrite existing files
2281 nNameClash = NameClash::OVERWRITE;
2285 OUString aMimeType = pImpl->getFilterMimeType();
2286 ::ucbhelper::InsertOperation eOperation = ::ucbhelper::InsertOperation::Copy;
2287 bool bMajor = false;
2288 OUString sComment;
2289 if ( IsInCheckIn( ) )
2291 eOperation = ::ucbhelper::InsertOperation::Checkin;
2292 const SfxBoolItem* pMajor = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOCINFO_MAJOR, false);
2293 bMajor = pMajor && pMajor->GetValue( );
2294 const SfxStringItem* pComments = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_DOCINFO_COMMENTS, false);
2295 if ( pComments )
2296 sComment = pComments->GetValue( );
2298 OUString sResultURL;
2299 aTransferContent.transferContent(
2300 aSourceContent, eOperation,
2301 aFileName, nNameClash, aMimeType, bMajor, sComment,
2302 &sResultURL, sObjectId );
2304 if ( !sResultURL.isEmpty( ) ) // Likely to happen only for checkin
2305 SwitchDocumentToFile( sResultURL );
2308 if ( GetURLObject().isAnyKnownWebDAVScheme() &&
2309 eOperation == ::ucbhelper::InsertOperation::Copy )
2311 // tdf#95272 try to re-issue a lock command when a new file is created.
2312 // This may be needed because some WebDAV servers fail to implement the
2313 // 'LOCK on unallocated reference', see issue comment:
2314 // <https://bugs.documentfoundation.org/show_bug.cgi?id=95792#c8>
2315 // and specification at:
2316 // <http://tools.ietf.org/html/rfc4918#section-7.3>
2317 // If the WebDAV resource is already locked by this LO instance, nothing will
2318 // happen, e.g. the LOCK method will not be sent to the server.
2319 ::ucbhelper::Content aLockContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
2320 aLockContent.lock();
2323 catch ( css::uno::Exception & )
2325 TOOLS_WARN_EXCEPTION( "sfx.doc", "LOCK not working while re-issuing it" );
2328 catch ( const css::ucb::CommandAbortedException& )
2330 pImpl->m_eError = ERRCODE_ABORT;
2332 catch ( const css::ucb::CommandFailedException& )
2334 pImpl->m_eError = ERRCODE_ABORT;
2336 catch ( const css::ucb::InteractiveIOException& r )
2338 if ( r.Code == IOErrorCode_ACCESS_DENIED )
2339 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
2340 else if ( r.Code == IOErrorCode_NOT_EXISTING )
2341 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2342 else if ( r.Code == IOErrorCode_CANT_READ )
2343 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2344 else
2345 pImpl->m_eError = ERRCODE_IO_GENERAL;
2347 catch ( const css::uno::Exception& )
2349 pImpl->m_eError = ERRCODE_IO_GENERAL;
2352 // do not switch from temporary file in case of nonfile protocol
2356 if ( ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) && !pImpl->pTempFile )
2358 // without a TempFile the physical and logical name should be the same after successful transfer
2359 if (osl::FileBase::getSystemPathFromFileURL(
2360 GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName )
2361 != osl::FileBase::E_None)
2363 pImpl->m_aName.clear();
2365 pImpl->m_bSalvageMode = false;
2370 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent,
2371 const OUString& aPrefix,
2372 const OUString& aExtension,
2373 const OUString& aDestDir )
2375 if ( !pImpl->m_aBackupURL.isEmpty() )
2376 return; // the backup was done already
2378 ::utl::TempFile aTransactTemp( aPrefix, true, &aExtension, &aDestDir );
2380 INetURLObject aBackObj( aTransactTemp.GetURL() );
2381 OUString aBackupName = aBackObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2383 Reference < css::ucb::XCommandEnvironment > xDummyEnv;
2384 ::ucbhelper::Content aBackupCont;
2385 if( ::ucbhelper::Content::create( aDestDir, xDummyEnv, comphelper::getProcessComponentContext(), aBackupCont ) )
2389 OUString sMimeType = pImpl->getFilterMimeType();
2390 aBackupCont.transferContent( aOriginalContent,
2391 ::ucbhelper::InsertOperation::Copy,
2392 aBackupName,
2393 NameClash::OVERWRITE,
2394 sMimeType );
2395 pImpl->m_aBackupURL = aBackObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2396 pImpl->m_bRemoveBackup = true;
2398 catch( const Exception& )
2402 if ( pImpl->m_aBackupURL.isEmpty() )
2403 aTransactTemp.EnableKillingFile();
2407 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent )
2409 if ( !pImpl->m_aBackupURL.isEmpty() )
2410 return; // the backup was done already
2412 OUString aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT,
2413 true,
2414 INetURLObject::DecodeMechanism::NONE );
2416 sal_Int32 nPrefixLen = aFileName.lastIndexOf( '.' );
2417 OUString aPrefix = ( nPrefixLen == -1 ) ? aFileName : aFileName.copy( 0, nPrefixLen );
2418 OUString aExtension = ( nPrefixLen == -1 ) ? OUString() : aFileName.copy( nPrefixLen );
2419 OUString aBakDir = SvtPathOptions().GetBackupPath();
2421 // create content for the parent folder ( = backup folder )
2422 ::ucbhelper::Content aContent;
2423 Reference < css::ucb::XCommandEnvironment > xEnv;
2424 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
2425 DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aBakDir );
2427 if ( !pImpl->m_aBackupURL.isEmpty() )
2428 return;
2430 // the copying to the backup catalog failed ( for example because
2431 // of using an encrypted partition as target catalog )
2432 // since the user did not specify to make backup explicitly
2433 // office should try to make backup in another place,
2434 // target catalog does not look bad for this case ( and looks
2435 // to be the only way for encrypted partitions )
2437 INetURLObject aDest = GetURLObject();
2438 if ( aDest.removeSegment() )
2439 DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2443 void SfxMedium::DoBackup_Impl()
2445 // source file name is the logical name of this medium
2446 INetURLObject aSource( GetURLObject() );
2448 // there is nothing to backup in case source file does not exist
2449 if ( !::utl::UCBContentHelper::IsDocument( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
2450 return;
2452 bool bSuccess = false;
2454 // get path for backups
2455 OUString aBakDir = SvtPathOptions().GetBackupPath();
2456 if( !aBakDir.isEmpty() )
2458 // create content for the parent folder ( = backup folder )
2459 ::ucbhelper::Content aContent;
2460 Reference < css::ucb::XCommandEnvironment > xEnv;
2461 if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
2463 // save as ".bak" file
2464 INetURLObject aDest( aBakDir );
2465 aDest.insertName( aSource.getName() );
2466 aDest.setExtension( "bak" );
2467 OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2469 // create a content for the source file
2470 ::ucbhelper::Content aSourceContent;
2471 if ( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) )
2475 // do the transfer ( copy source file to backup dir )
2476 OUString sMimeType = pImpl->getFilterMimeType();
2477 aContent.transferContent( aSourceContent,
2478 ::ucbhelper::InsertOperation::Copy,
2479 aFileName,
2480 NameClash::OVERWRITE,
2481 sMimeType );
2482 pImpl->m_aBackupURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2483 pImpl->m_bRemoveBackup = false;
2484 bSuccess = true;
2486 catch ( const css::uno::Exception& )
2493 if ( !bSuccess )
2495 pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
2500 void SfxMedium::ClearBackup_Impl()
2502 if( pImpl->m_bRemoveBackup )
2504 // currently a document is always stored in a new medium,
2505 // thus if a backup can not be removed the backup URL should not be cleaned
2506 if ( !pImpl->m_aBackupURL.isEmpty() )
2508 if ( ::utl::UCBContentHelper::Kill( pImpl->m_aBackupURL ) )
2510 pImpl->m_bRemoveBackup = false;
2511 pImpl->m_aBackupURL.clear();
2513 else
2516 SAL_WARN( "sfx.doc", "Couldn't remove backup file!");
2520 else
2521 pImpl->m_aBackupURL.clear();
2525 void SfxMedium::GetLockingStream_Impl()
2527 if ( GetURLObject().GetProtocol() != INetProtocol::File
2528 || pImpl->m_xLockingStream.is() )
2529 return;
2531 const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
2532 if ( pWriteStreamItem )
2533 pWriteStreamItem->GetValue() >>= pImpl->m_xLockingStream;
2535 if ( pImpl->m_xLockingStream.is() )
2536 return;
2538 // open the original document
2539 uno::Sequence< beans::PropertyValue > xProps;
2540 TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
2541 utl::MediaDescriptor aMedium( xProps );
2543 aMedium.addInputStreamOwnLock();
2545 uno::Reference< io::XInputStream > xInputStream;
2546 aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->m_xLockingStream;
2547 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= xInputStream;
2549 if ( !pImpl->pTempFile && pImpl->m_aName.isEmpty() )
2551 // the medium is still based on the original file, it makes sense to initialize the streams
2552 if ( pImpl->m_xLockingStream.is() )
2553 pImpl->xStream = pImpl->m_xLockingStream;
2555 if ( xInputStream.is() )
2556 pImpl->xInputStream = xInputStream;
2558 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2559 pImpl->xInputStream = pImpl->xStream->getInputStream();
2564 void SfxMedium::GetMedium_Impl()
2566 if ( pImpl->m_pInStream
2567 && (!pImpl->bIsTemp || pImpl->xInputStream.is() || pImpl->m_xInputStreamToLoadFrom.is() || pImpl->xStream.is() || pImpl->m_xLockingStream.is() ) )
2568 return;
2570 pImpl->bDownloadDone = false;
2571 Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
2573 //TODO/MBA: need support for SID_STREAM
2574 const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
2575 const SfxUnoAnyItem* pInStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INPUTSTREAM, false);
2576 if ( pWriteStreamItem )
2578 pWriteStreamItem->GetValue() >>= pImpl->xStream;
2580 if ( pInStreamItem )
2581 pInStreamItem->GetValue() >>= pImpl->xInputStream;
2583 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2584 pImpl->xInputStream = pImpl->xStream->getInputStream();
2586 else if ( pInStreamItem )
2588 pInStreamItem->GetValue() >>= pImpl->xInputStream;
2590 else
2592 uno::Sequence < beans::PropertyValue > xProps;
2593 OUString aFileName;
2594 if (!pImpl->m_aName.isEmpty())
2596 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aFileName )
2597 != osl::FileBase::E_None )
2599 SAL_WARN( "sfx.doc", "Physical name not convertible!");
2602 else
2603 aFileName = GetName();
2605 // in case the temporary file exists the streams should be initialized from it,
2606 // but the original MediaDescriptor should not be changed
2607 bool bFromTempFile = ( pImpl->pTempFile != nullptr );
2609 if ( !bFromTempFile )
2611 GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, aFileName ) );
2612 if( !(pImpl->m_nStorOpenMode & StreamMode::WRITE) )
2613 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
2614 if (xInteractionHandler.is())
2615 GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, makeAny(xInteractionHandler) ) );
2618 if ( pImpl->m_xInputStreamToLoadFrom.is() )
2620 pImpl->xInputStream = pImpl->m_xInputStreamToLoadFrom;
2621 if (pImpl->m_bInputStreamIsReadOnly)
2622 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
2624 else
2626 TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
2627 utl::MediaDescriptor aMedium( xProps );
2629 if ( pImpl->m_xLockingStream.is() && !bFromTempFile )
2631 // the medium is not based on the temporary file, so the original stream can be used
2632 pImpl->xStream = pImpl->m_xLockingStream;
2634 else
2636 if ( bFromTempFile )
2638 aMedium[utl::MediaDescriptor::PROP_URL()] <<= aFileName;
2639 aMedium.erase( utl::MediaDescriptor::PROP_READONLY() );
2640 aMedium.addInputStream();
2642 else if ( GetURLObject().GetProtocol() == INetProtocol::File )
2644 // use the special locking approach only for file URLs
2645 aMedium.addInputStreamOwnLock();
2647 else
2649 // add a check for protocol, if it's http or https or provide webdav then add
2650 // the interaction handler to be used by the authentication dialog
2651 if ( GetURLObject().isAnyKnownWebDAVScheme() )
2653 aMedium[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER()] <<= GetInteractionHandler( true );
2655 aMedium.addInputStream();
2657 // the ReadOnly property set in aMedium is ignored
2658 // the check is done in LockOrigFileOnDemand() for file and non-file URLs
2660 //TODO/MBA: what happens if property is not there?!
2661 aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->xStream;
2662 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= pImpl->xInputStream;
2665 GetContent();
2666 if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2667 pImpl->xInputStream = pImpl->xStream->getInputStream();
2670 if ( !bFromTempFile )
2672 //TODO/MBA: need support for SID_STREAM
2673 if ( pImpl->xStream.is() )
2674 GetItemSet()->Put( SfxUnoAnyItem( SID_STREAM, makeAny( pImpl->xStream ) ) );
2676 GetItemSet()->Put( SfxUnoAnyItem( SID_INPUTSTREAM, makeAny( pImpl->xInputStream ) ) );
2680 //TODO/MBA: ErrorHandling - how to transport error from MediaDescriptor
2681 if ( !GetError() && !pImpl->xStream.is() && !pImpl->xInputStream.is() )
2682 SetError(ERRCODE_IO_ACCESSDENIED);
2684 if ( !GetError() && !pImpl->m_pInStream )
2686 if ( pImpl->xStream.is() )
2687 pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xStream );
2688 else if ( pImpl->xInputStream.is() )
2689 pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xInputStream );
2692 pImpl->bDownloadDone = true;
2693 pImpl->aDoneLink.ClearPendingCall();
2694 ErrCode nError = GetError();
2695 pImpl->aDoneLink.Call( reinterpret_cast<void*>(sal_uInt32(nError)) );
2698 bool SfxMedium::IsRemote() const
2700 return pImpl->m_bRemote;
2703 void SfxMedium::SetUpdatePickList(bool bVal)
2705 pImpl->bUpdatePickList = bVal;
2708 bool SfxMedium::IsUpdatePickList() const
2710 return pImpl->bUpdatePickList;
2713 void SfxMedium::SetLongName(const OUString &rName)
2715 pImpl->m_aLongName = rName;
2718 const OUString& SfxMedium::GetLongName() const
2720 return pImpl->m_aLongName;
2723 void SfxMedium::SetDoneLink( const Link<void*,void>& rLink )
2725 pImpl->aDoneLink = rLink;
2728 void SfxMedium::Download( const Link<void*,void>& aLink )
2730 SetDoneLink( aLink );
2731 GetInStream();
2732 if ( pImpl->m_pInStream && !aLink.IsSet() )
2734 while( !pImpl->bDownloadDone )
2735 Application::Yield();
2740 void SfxMedium::Init_Impl()
2741 /* [Description]
2742 Includes a valid:: sun:: com:: star:: util:: URL (If a file name was
2743 previously in there) in the logical name and if available sets the
2744 physical name as the file name.
2748 Reference< XOutputStream > rOutStream;
2750 // TODO/LATER: handle lifetime of storages
2751 pImpl->bDisposeStorage = false;
2753 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
2754 if ( pSalvageItem && pSalvageItem->GetValue().isEmpty() )
2756 pSalvageItem = nullptr;
2757 pImpl->m_pSet->ClearItem( SID_DOC_SALVAGE );
2760 if (!pImpl->m_aLogicName.isEmpty())
2762 INetURLObject aUrl( pImpl->m_aLogicName );
2763 INetProtocol eProt = aUrl.GetProtocol();
2764 if ( eProt == INetProtocol::NotValid )
2766 SAL_WARN( "sfx.doc", "URL <" << pImpl->m_aLogicName << "> with unknown protocol" );
2768 else
2770 if ( aUrl.HasMark() )
2772 pImpl->m_aLogicName = aUrl.GetURLNoMark( INetURLObject::DecodeMechanism::NONE );
2773 GetItemSet()->Put( SfxStringItem( SID_JUMPMARK, aUrl.GetMark() ) );
2776 // try to convert the URL into a physical name - but never change a physical name
2777 // physical name may be set if the logical name is changed after construction
2778 if ( pImpl->m_aName.isEmpty() )
2779 osl::FileBase::getSystemPathFromFileURL( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName );
2780 else
2782 DBG_ASSERT( pSalvageItem, "Suspicious change of logical name!" );
2787 if ( pSalvageItem && !pSalvageItem->GetValue().isEmpty() )
2789 pImpl->m_aLogicName = pSalvageItem->GetValue();
2790 pImpl->m_pURLObj.reset();
2791 pImpl->m_bSalvageMode = true;
2794 // in case output stream is by mistake here
2795 // clear the reference
2796 const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
2797 if( pOutStreamItem
2798 && ( !( pOutStreamItem->GetValue() >>= rOutStream )
2799 || !pImpl->m_aLogicName.startsWith("private:stream")) )
2801 pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
2802 SAL_WARN( "sfx.doc", "Unexpected Output stream parameter!" );
2805 if (!pImpl->m_aLogicName.isEmpty())
2807 // if the logic name is set it should be set in MediaDescriptor as well
2808 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
2809 if ( !pFileNameItem )
2811 // let the ItemSet be created if necessary
2812 GetItemSet()->Put(
2813 SfxStringItem(
2814 SID_FILE_NAME, INetURLObject( pImpl->m_aLogicName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
2818 SetIsRemote_Impl();
2820 osl::DirectoryItem item;
2821 if (osl::DirectoryItem::get(GetName(), item) == osl::FileBase::E_None) {
2822 osl::FileStatus stat(osl_FileStatus_Mask_Attributes);
2823 if (item.getFileStatus(stat) == osl::FileBase::E_None
2824 && stat.isValid(osl_FileStatus_Mask_Attributes))
2826 if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0) {
2827 pImpl->m_bOriginallyReadOnly = true;
2834 SfxMedium::SfxMedium() : pImpl(new SfxMedium_Impl)
2836 Init_Impl();
2840 void SfxMedium::UseInteractionHandler( bool bUse )
2842 pImpl->bAllowDefaultIntHdl = bUse;
2846 css::uno::Reference< css::task::XInteractionHandler >
2847 SfxMedium::GetInteractionHandler( bool bGetAlways )
2849 // if interaction isn't allowed explicitly ... return empty reference!
2850 if ( !bGetAlways && !pImpl->bUseInteractionHandler )
2851 return css::uno::Reference< css::task::XInteractionHandler >();
2853 // search a possible existing handler inside cached item set
2854 if ( pImpl->m_pSet )
2856 css::uno::Reference< css::task::XInteractionHandler > xHandler;
2857 const SfxUnoAnyItem* pHandler = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INTERACTIONHANDLER, false);
2858 if ( pHandler && (pHandler->GetValue() >>= xHandler) && xHandler.is() )
2859 return xHandler;
2862 // if default interaction isn't allowed explicitly ... return empty reference!
2863 if ( !bGetAlways && !pImpl->bAllowDefaultIntHdl )
2864 return css::uno::Reference< css::task::XInteractionHandler >();
2866 // otherwise return cached default handler ... if it exist.
2867 if ( pImpl->xInteraction.is() )
2868 return pImpl->xInteraction;
2870 // create default handler and cache it!
2871 Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2872 pImpl->xInteraction.set(
2873 task::InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
2874 return pImpl->xInteraction;
2878 void SfxMedium::SetFilter( const std::shared_ptr<const SfxFilter>& pFilter )
2880 pImpl->m_pFilter = pFilter;
2883 const std::shared_ptr<const SfxFilter>& SfxMedium::GetFilter() const
2885 return pImpl->m_pFilter;
2888 sal_uInt32 SfxMedium::CreatePasswordToModifyHash( const OUString& aPasswd, bool bWriter )
2890 sal_uInt32 nHash = 0;
2892 if ( !aPasswd.isEmpty() )
2894 if ( bWriter )
2896 nHash = ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd );
2898 else
2900 rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
2901 nHash = ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd, nEncoding );
2905 return nHash;
2909 void SfxMedium::Close(bool bInDestruction)
2911 if ( pImpl->xStorage.is() )
2913 CloseStorage();
2916 CloseStreams_Impl(bInDestruction);
2918 UnlockFile( false );
2921 void SfxMedium::CloseAndRelease()
2923 if ( pImpl->xStorage.is() )
2925 CloseStorage();
2928 CloseAndReleaseStreams_Impl();
2930 UnlockFile( true );
2933 void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV )
2935 pImpl->m_bDisableUnlockWebDAV = bDisableUnlockWebDAV;
2938 void SfxMedium::DisableFileSync(bool bDisableFileSync)
2940 pImpl->m_bDisableFileSync = bDisableFileSync;
2943 void SfxMedium::UnlockFile( bool bReleaseLockStream )
2945 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
2946 (void) bReleaseLockStream;
2947 #else
2948 // check if webdav
2949 if ( GetURLObject().isAnyKnownWebDAVScheme() )
2951 // do nothing if WebDAV locking if disabled
2952 // (shouldn't happen because we already skipped locking,
2953 // see LockOrigFileOnDemand, but just in case ...)
2954 if (!IsWebDAVLockingUsed())
2955 return;
2957 if ( pImpl->m_bLocked )
2959 // an interaction handler should be used for authentication, if needed
2960 try {
2961 uno::Reference< css::task::XInteractionHandler > xHandler = GetInteractionHandler( true );
2962 uno::Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment( xHandler,
2963 Reference< css::ucb::XProgressHandler >() );
2964 ucbhelper::Content aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext());
2965 pImpl->m_bLocked = false;
2966 //check if WebDAV unlock was explicitly disabled
2967 if ( !pImpl->m_bDisableUnlockWebDAV )
2968 aContentToUnlock.unlock();
2970 catch ( uno::Exception& )
2972 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
2975 return;
2978 if ( pImpl->m_xLockingStream.is() )
2980 if ( bReleaseLockStream )
2984 uno::Reference< io::XInputStream > xInStream = pImpl->m_xLockingStream->getInputStream();
2985 uno::Reference< io::XOutputStream > xOutStream = pImpl->m_xLockingStream->getOutputStream();
2986 if ( xInStream.is() )
2987 xInStream->closeInput();
2988 if ( xOutStream.is() )
2989 xOutStream->closeOutput();
2991 catch( const uno::Exception& )
2995 pImpl->m_xLockingStream.clear();
2998 if ( !pImpl->m_bLocked )
2999 return;
3001 ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
3005 pImpl->m_bLocked = false;
3006 // TODO/LATER: A warning could be shown in case the file is not the own one
3007 aLockFile.RemoveFile();
3009 catch( const io::WrongFormatException& )
3013 // erase the empty or corrupt file
3014 aLockFile.RemoveFileDirectly();
3016 catch( const uno::Exception& )
3019 catch( const uno::Exception& )
3022 if(!pImpl->m_bMSOLockFileCreated)
3023 return;
3025 ::svt::MSODocumentLockFile aMSOLockFile( pImpl->m_aLogicName );
3029 pImpl->m_bLocked = false;
3030 // TODO/LATER: A warning could be shown in case the file is not the own one
3031 aMSOLockFile.RemoveFile();
3033 catch( const io::WrongFormatException& )
3037 // erase the empty or corrupt file
3038 aMSOLockFile.RemoveFileDirectly();
3040 catch( const uno::Exception& )
3043 catch( const uno::Exception& )
3045 pImpl->m_bMSOLockFileCreated = false;
3046 #endif
3049 void SfxMedium::CloseAndReleaseStreams_Impl()
3051 CloseZipStorage_Impl();
3053 uno::Reference< io::XInputStream > xInToClose = pImpl->xInputStream;
3054 uno::Reference< io::XOutputStream > xOutToClose;
3055 if ( pImpl->xStream.is() )
3057 xOutToClose = pImpl->xStream->getOutputStream();
3059 // if the locking stream is closed here the related member should be cleaned
3060 if ( pImpl->xStream == pImpl->m_xLockingStream )
3061 pImpl->m_xLockingStream.clear();
3064 // The probably existing SvStream wrappers should be closed first
3065 CloseStreams_Impl();
3067 // in case of salvage mode the storage is based on the streams
3068 if ( pImpl->m_bSalvageMode )
3069 return;
3073 if ( xInToClose.is() )
3074 xInToClose->closeInput();
3075 if ( xOutToClose.is() )
3076 xOutToClose->closeOutput();
3078 catch ( const uno::Exception& )
3084 void SfxMedium::CloseStreams_Impl(bool bInDestruction)
3086 CloseInStream_Impl(bInDestruction);
3087 CloseOutStream_Impl();
3089 if ( pImpl->m_pSet )
3090 pImpl->m_pSet->ClearItem( SID_CONTENT );
3092 pImpl->aContent = ::ucbhelper::Content();
3096 void SfxMedium::SetIsRemote_Impl()
3098 INetURLObject aObj( GetName() );
3099 switch( aObj.GetProtocol() )
3101 case INetProtocol::Ftp:
3102 case INetProtocol::Http:
3103 case INetProtocol::Https:
3104 pImpl->m_bRemote = true;
3105 break;
3106 default:
3107 pImpl->m_bRemote = GetName().startsWith("private:msgid");
3108 break;
3111 // As files that are written to the remote transmission must also be able
3112 // to be read.
3113 if (pImpl->m_bRemote)
3114 pImpl->m_nStorOpenMode |= StreamMode::READ;
3118 void SfxMedium::SetName( const OUString& aNameP, bool bSetOrigURL )
3120 if (pImpl->aOrigURL.isEmpty())
3121 pImpl->aOrigURL = pImpl->m_aLogicName;
3122 if( bSetOrigURL )
3123 pImpl->aOrigURL = aNameP;
3124 pImpl->m_aLogicName = aNameP;
3125 pImpl->m_pURLObj.reset();
3126 pImpl->aContent = ::ucbhelper::Content();
3127 Init_Impl();
3131 const OUString& SfxMedium::GetOrigURL() const
3133 return pImpl->aOrigURL.isEmpty() ? pImpl->m_aLogicName : pImpl->aOrigURL;
3137 void SfxMedium::SetPhysicalName_Impl( const OUString& rNameP )
3139 if ( rNameP != pImpl->m_aName )
3141 pImpl->pTempFile.reset();
3143 if ( !pImpl->m_aName.isEmpty() || !rNameP.isEmpty() )
3144 pImpl->aContent = ::ucbhelper::Content();
3146 pImpl->m_aName = rNameP;
3147 pImpl->m_bTriedStorage = false;
3148 pImpl->bIsStorage = false;
3153 void SfxMedium::ReOpen()
3155 bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
3156 pImpl->bUseInteractionHandler = false;
3157 GetMedium_Impl();
3158 pImpl->bUseInteractionHandler = bUseInteractionHandler;
3162 void SfxMedium::CompleteReOpen()
3164 // do not use temporary file for reopen and in case of success throw the temporary file away
3165 bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
3166 pImpl->bUseInteractionHandler = false;
3168 std::unique_ptr<::utl::TempFile> pTmpFile;
3169 if ( pImpl->pTempFile )
3171 pTmpFile = std::move(pImpl->pTempFile);
3172 pImpl->m_aName.clear();
3175 GetMedium_Impl();
3177 if ( GetError() )
3179 if ( pImpl->pTempFile )
3181 pImpl->pTempFile->EnableKillingFile();
3182 pImpl->pTempFile.reset();
3184 pImpl->pTempFile = std::move( pTmpFile );
3185 if ( pImpl->pTempFile )
3186 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3188 else if (pTmpFile)
3190 pTmpFile->EnableKillingFile();
3191 pTmpFile.reset();
3194 pImpl->bUseInteractionHandler = bUseInteractionHandler;
3197 SfxMedium::SfxMedium(const OUString &rName, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, const std::shared_ptr<SfxItemSet>& pInSet) :
3198 pImpl(new SfxMedium_Impl)
3200 pImpl->m_pSet = pInSet;
3201 pImpl->m_pFilter = std::move(pFilter);
3202 pImpl->m_aLogicName = rName;
3203 pImpl->m_nStorOpenMode = nOpenMode;
3204 Init_Impl();
3207 SfxMedium::SfxMedium(const OUString &rName, const OUString &rReferer, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, const std::shared_ptr<SfxItemSet>& pInSet) :
3208 pImpl(new SfxMedium_Impl)
3210 pImpl->m_pSet = pInSet;
3211 SfxItemSet * s = GetItemSet();
3212 if (s->GetItem(SID_REFERER) == nullptr) {
3213 s->Put(SfxStringItem(SID_REFERER, rReferer));
3215 pImpl->m_pFilter = std::move(pFilter);
3216 pImpl->m_aLogicName = rName;
3217 pImpl->m_nStorOpenMode = nOpenMode;
3218 Init_Impl();
3221 SfxMedium::SfxMedium( const uno::Sequence<beans::PropertyValue>& aArgs ) :
3222 pImpl(new SfxMedium_Impl)
3224 SfxAllItemSet *pParams = new SfxAllItemSet( SfxGetpApp()->GetPool() );
3225 pImpl->m_pSet.reset( pParams );
3226 TransformParameters( SID_OPENDOC, aArgs, *pParams );
3227 SetArgs(aArgs);
3229 OUString aFilterProvider, aFilterName;
3231 const SfxPoolItem* pItem = nullptr;
3232 if (pImpl->m_pSet->HasItem(SID_FILTER_PROVIDER, &pItem))
3233 aFilterProvider = static_cast<const SfxStringItem*>(pItem)->GetValue();
3235 if (pImpl->m_pSet->HasItem(SID_FILTER_NAME, &pItem))
3236 aFilterName = static_cast<const SfxStringItem*>(pItem)->GetValue();
3239 if (aFilterProvider.isEmpty())
3241 // This is a conventional filter type.
3242 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( aFilterName );
3244 else
3246 // This filter is from an external provider such as orcus.
3247 pImpl->m_pCustomFilter = std::make_shared<SfxFilter>(aFilterProvider, aFilterName);
3248 pImpl->m_pFilter = pImpl->m_pCustomFilter;
3251 const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
3252 if( pSalvageItem )
3254 // QUESTION: there is some treatment of Salvage in Init_Impl; align!
3255 if ( !pSalvageItem->GetValue().isEmpty() )
3257 // if a URL is provided in SalvageItem that means that the FileName refers to a temporary file
3258 // that must be copied here
3260 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
3261 if (!pFileNameItem) throw uno::RuntimeException();
3262 OUString aNewTempFileURL = SfxMedium::CreateTempCopyWithExt( pFileNameItem->GetValue() );
3263 if ( !aNewTempFileURL.isEmpty() )
3265 pImpl->m_pSet->Put( SfxStringItem( SID_FILE_NAME, aNewTempFileURL ) );
3266 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
3267 pImpl->m_pSet->ClearItem( SID_STREAM );
3268 pImpl->m_pSet->ClearItem( SID_CONTENT );
3270 else
3272 SAL_WARN( "sfx.doc", "Can not create a new temporary file for crash recovery!" );
3277 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
3278 if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
3279 pImpl->m_bOriginallyLoadedReadOnly = true;
3281 const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
3282 if (!pFileNameItem) throw uno::RuntimeException();
3283 pImpl->m_aLogicName = pFileNameItem->GetValue();
3284 pImpl->m_nStorOpenMode = pImpl->m_bOriginallyLoadedReadOnly
3285 ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE;
3286 Init_Impl();
3289 void SfxMedium::SetArgs(const uno::Sequence<beans::PropertyValue>& rArgs)
3291 comphelper::SequenceAsHashMap aArgsMap(rArgs);
3292 aArgsMap.erase("Stream");
3293 aArgsMap.erase("InputStream");
3294 pImpl->m_aArgs = aArgsMap.getAsConstPropertyValueList();
3297 uno::Sequence<beans::PropertyValue> SfxMedium::GetArgs() const { return pImpl->m_aArgs; }
3299 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const std::shared_ptr<SfxItemSet>& p ) :
3300 pImpl(new SfxMedium_Impl)
3302 OUString aType = SfxFilter::GetTypeFromStorage(rStor);
3303 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( aType );
3304 DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
3306 Init_Impl();
3307 pImpl->xStorage = rStor;
3308 pImpl->bDisposeStorage = false;
3310 // always take BaseURL first, could be overwritten by ItemSet
3311 GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
3312 if ( p )
3313 GetItemSet()->Put( *p );
3317 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const OUString &rTypeName, const std::shared_ptr<SfxItemSet>& p ) :
3318 pImpl(new SfxMedium_Impl)
3320 pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( rTypeName );
3321 DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
3323 Init_Impl();
3324 pImpl->xStorage = rStor;
3325 pImpl->bDisposeStorage = false;
3327 // always take BaseURL first, could be overwritten by ItemSet
3328 GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
3329 if ( p )
3330 GetItemSet()->Put( *p );
3334 SfxMedium::~SfxMedium()
3336 // if there is a requirement to clean the backup this is the last possibility to do it
3337 ClearBackup_Impl();
3339 Close(/*bInDestruction*/true);
3341 if( !pImpl->bIsTemp || pImpl->m_aName.isEmpty() )
3342 return;
3344 OUString aTemp;
3345 if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aTemp )
3346 != osl::FileBase::E_None )
3348 SAL_WARN( "sfx.doc", "Physical name not convertible!");
3351 if ( !::utl::UCBContentHelper::Kill( aTemp ) )
3353 SAL_WARN( "sfx.doc", "Couldn't remove temporary file!");
3357 const OUString& SfxMedium::GetName() const
3359 return pImpl->m_aLogicName;
3362 const INetURLObject& SfxMedium::GetURLObject() const
3364 if (!pImpl->m_pURLObj)
3366 pImpl->m_pURLObj.reset( new INetURLObject( pImpl->m_aLogicName ) );
3367 pImpl->m_pURLObj->SetMark("");
3370 return *pImpl->m_pURLObj;
3373 void SfxMedium::SetExpired_Impl( const DateTime& rDateTime )
3375 pImpl->aExpireTime = rDateTime;
3379 bool SfxMedium::IsExpired() const
3381 return pImpl->aExpireTime.IsValidAndGregorian() && pImpl->aExpireTime < DateTime( DateTime::SYSTEM );
3385 SfxFrame* SfxMedium::GetLoadTargetFrame() const
3387 return pImpl->wLoadTargetFrame;
3390 void SfxMedium::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly )
3392 pImpl->m_xInputStreamToLoadFrom = xInputStream;
3393 pImpl->m_bInputStreamIsReadOnly = bIsReadOnly;
3396 void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame )
3398 pImpl->wLoadTargetFrame = pFrame;
3402 void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& rStor )
3404 pImpl->xStorage = rStor;
3408 SfxItemSet* SfxMedium::GetItemSet() const
3410 // this method *must* return an ItemSet, returning NULL can cause crashes
3411 if (!pImpl->m_pSet)
3412 pImpl->m_pSet = std::make_shared<SfxAllItemSet>( SfxGetpApp()->GetPool() );
3413 return pImpl->m_pSet.get();
3417 SvKeyValueIterator* SfxMedium::GetHeaderAttributes_Impl()
3419 if( !pImpl->xAttributes.is() )
3421 pImpl->xAttributes = SvKeyValueIteratorRef( new SvKeyValueIterator );
3423 if ( GetContent().is() )
3427 Any aAny = pImpl->aContent.getPropertyValue("MediaType");
3428 OUString aContentType;
3429 aAny >>= aContentType;
3431 pImpl->xAttributes->Append( SvKeyValue( "content-type", aContentType ) );
3433 catch ( const css::uno::Exception& )
3439 return pImpl->xAttributes.get();
3442 css::uno::Reference< css::io::XInputStream > const & SfxMedium::GetInputStream()
3444 if ( !pImpl->xInputStream.is() )
3445 GetMedium_Impl();
3446 return pImpl->xInputStream;
3449 const uno::Sequence < util::RevisionTag >& SfxMedium::GetVersionList( bool _bNoReload )
3451 // if the medium has no name, then this medium should represent a new document and can have no version info
3452 if ( ( !_bNoReload || !pImpl->m_bVersionsAlreadyLoaded ) && !pImpl->aVersions.hasElements() &&
3453 ( !pImpl->m_aName.isEmpty() || !pImpl->m_aLogicName.isEmpty() ) && GetStorage().is() )
3455 uno::Reference < document::XDocumentRevisionListPersistence > xReader =
3456 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3459 pImpl->aVersions = xReader->load( GetStorage() );
3461 catch ( const uno::Exception& )
3466 if ( !pImpl->m_bVersionsAlreadyLoaded )
3467 pImpl->m_bVersionsAlreadyLoaded = true;
3469 return pImpl->aVersions;
3472 uno::Sequence < util::RevisionTag > SfxMedium::GetVersionList( const uno::Reference < embed::XStorage >& xStorage )
3474 uno::Reference < document::XDocumentRevisionListPersistence > xReader =
3475 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3478 return xReader->load( xStorage );
3480 catch ( const uno::Exception& )
3484 return uno::Sequence < util::RevisionTag >();
3487 void SfxMedium::AddVersion_Impl( util::RevisionTag& rRevision )
3489 if ( !GetStorage().is() )
3490 return;
3492 // To determine a unique name for the stream
3493 std::vector<sal_uInt32> aLongs;
3494 sal_Int32 nLength = pImpl->aVersions.getLength();
3495 for ( const auto& rVersion : std::as_const(pImpl->aVersions) )
3497 sal_uInt32 nVer = static_cast<sal_uInt32>( rVersion.Identifier.copy(7).toInt32());
3498 size_t n;
3499 for ( n=0; n<aLongs.size(); ++n )
3500 if ( nVer<aLongs[n] )
3501 break;
3503 aLongs.insert( aLongs.begin()+n, nVer );
3506 std::vector<sal_uInt32>::size_type nKey;
3507 for ( nKey=0; nKey<aLongs.size(); ++nKey )
3508 if ( aLongs[nKey] > nKey+1 )
3509 break;
3511 OUString aRevName = "Version" + OUString::number( nKey + 1 );
3512 pImpl->aVersions.realloc( nLength+1 );
3513 rRevision.Identifier = aRevName;
3514 pImpl->aVersions[nLength] = rRevision;
3517 void SfxMedium::RemoveVersion_Impl( const OUString& rName )
3519 if ( !pImpl->aVersions.hasElements() )
3520 return;
3522 auto pVersion = std::find_if(pImpl->aVersions.begin(), pImpl->aVersions.end(),
3523 [&rName](const auto& rVersion) { return rVersion.Identifier == rName; });
3524 if (pVersion != pImpl->aVersions.end())
3526 auto nIndex = static_cast<sal_Int32>(std::distance(pImpl->aVersions.begin(), pVersion));
3527 comphelper::removeElementAt(pImpl->aVersions, nIndex);
3531 bool SfxMedium::TransferVersionList_Impl( SfxMedium const & rMedium )
3533 if ( rMedium.pImpl->aVersions.hasElements() )
3535 pImpl->aVersions = rMedium.pImpl->aVersions;
3536 return true;
3539 return false;
3542 void SfxMedium::SaveVersionList_Impl()
3544 if ( !GetStorage().is() )
3545 return;
3547 if ( !pImpl->aVersions.hasElements() )
3548 return;
3550 uno::Reference < document::XDocumentRevisionListPersistence > xWriter =
3551 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3554 xWriter->store( GetStorage(), pImpl->aVersions );
3556 catch ( const uno::Exception& )
3561 bool SfxMedium::IsReadOnly() const
3563 // a) ReadOnly filter can't produce read/write contents!
3564 bool bReadOnly = pImpl->m_pFilter && (pImpl->m_pFilter->GetFilterFlags() & SfxFilterFlags::OPENREADONLY);
3566 // b) if filter allow read/write contents .. check open mode of the storage
3567 if (!bReadOnly)
3568 bReadOnly = !( GetOpenMode() & StreamMode::WRITE );
3570 // c) the API can force the readonly state!
3571 if (!bReadOnly)
3573 const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOC_READONLY, false);
3574 if (pItem)
3575 bReadOnly = pItem->GetValue();
3578 return bReadOnly;
3581 bool SfxMedium::IsOriginallyReadOnly() const
3583 return pImpl->m_bOriginallyReadOnly;
3586 bool SfxMedium::IsOriginallyLoadedReadOnly() const
3588 return pImpl->m_bOriginallyLoadedReadOnly;
3591 bool SfxMedium::SetWritableForUserOnly( const OUString& aURL )
3593 // UCB does not allow to allow write access only for the user,
3594 // use osl API
3595 bool bResult = false;
3597 ::osl::DirectoryItem aDirItem;
3598 if ( ::osl::DirectoryItem::get( aURL, aDirItem ) == ::osl::FileBase::E_None )
3600 ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Attributes );
3601 if ( aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None
3602 && aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) )
3604 sal_uInt64 nAttributes = aFileStatus.getAttributes();
3606 nAttributes &= ~(osl_File_Attribute_OwnWrite |
3607 osl_File_Attribute_GrpWrite |
3608 osl_File_Attribute_OthWrite |
3609 osl_File_Attribute_ReadOnly);
3610 nAttributes |= (osl_File_Attribute_OwnWrite |
3611 osl_File_Attribute_OwnRead);
3613 bResult = ( osl::File::setAttributes( aURL, nAttributes ) == ::osl::FileBase::E_None );
3617 return bResult;
3620 namespace
3622 /// Get the parent directory of a temporary file for output purposes.
3623 OUString GetLogicBase(const INetURLObject& rURL, std::unique_ptr<SfxMedium_Impl> const & pImpl)
3625 OUString aLogicBase;
3627 #if HAVE_FEATURE_MACOSX_SANDBOX
3628 // In a sandboxed environment we don't want to attempt to create temporary files in the same
3629 // directory where the user has selected an output file to be stored. The sandboxed process has
3630 // permission only to create the specifically named output file in that directory.
3631 (void) rURL;
3632 (void) pImpl;
3633 #else
3635 if (rURL.GetProtocol() == INetProtocol::File && !pImpl->m_pInStream)
3637 // Try to create the temp file in the same directory when storing.
3638 INetURLObject aURL(rURL);
3639 aURL.removeSegment();
3640 aLogicBase = aURL.GetMainURL(INetURLObject::DecodeMechanism::WithCharset);
3643 if (pImpl->m_bHasEmbeddedObjects)
3644 // Embedded objects would mean a special base, ignore that.
3645 aLogicBase.clear();
3647 #endif // !HAVE_FEATURE_MACOSX_SANDBOX
3649 return aLogicBase;
3653 void SfxMedium::CreateTempFile( bool bReplace )
3655 if ( pImpl->pTempFile )
3657 if ( !bReplace )
3658 return;
3660 pImpl->pTempFile.reset();
3661 pImpl->m_aName.clear();
3664 OUString aLogicBase = GetLogicBase(GetURLObject(), pImpl);
3665 pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) );
3666 pImpl->pTempFile->EnableKillingFile();
3667 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3668 OUString aTmpURL = pImpl->pTempFile->GetURL();
3669 if ( pImpl->m_aName.isEmpty() || aTmpURL.isEmpty() )
3671 SetError(ERRCODE_IO_CANTWRITE);
3672 return;
3675 if ( !(pImpl->m_nStorOpenMode & StreamMode::TRUNC) )
3677 bool bTransferSuccess = false;
3679 if ( GetContent().is()
3680 && GetURLObject().GetProtocol() == INetProtocol::File
3681 && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
3683 // if there is already such a document, we should copy it
3684 // if it is a file system use OS copy process
3687 uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
3688 INetURLObject aTmpURLObj( aTmpURL );
3689 OUString aFileName = aTmpURLObj.getName( INetURLObject::LAST_SEGMENT,
3690 true,
3691 INetURLObject::DecodeMechanism::WithCharset );
3692 if ( !aFileName.isEmpty() && aTmpURLObj.removeSegment() )
3694 ::ucbhelper::Content aTargetContent( aTmpURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
3695 OUString sMimeType = pImpl->getFilterMimeType();
3696 aTargetContent.transferContent( pImpl->aContent, ::ucbhelper::InsertOperation::Copy, aFileName, NameClash::OVERWRITE, sMimeType );
3697 SetWritableForUserOnly( aTmpURL );
3698 bTransferSuccess = true;
3701 catch( const uno::Exception& )
3704 if ( bTransferSuccess )
3706 CloseOutStream();
3707 CloseInStream();
3711 if ( !bTransferSuccess && pImpl->m_pInStream )
3713 // the case when there is no URL-access available or this is a remote protocol
3714 // but there is an input stream
3715 GetOutStream();
3716 if ( pImpl->m_pOutStream )
3718 std::unique_ptr<char[]> pBuf(new char [8192]);
3719 ErrCode nErr = ERRCODE_NONE;
3721 pImpl->m_pInStream->Seek(0);
3722 pImpl->m_pOutStream->Seek(0);
3724 while( !pImpl->m_pInStream->eof() && nErr == ERRCODE_NONE )
3726 sal_uInt32 nRead = pImpl->m_pInStream->ReadBytes(pBuf.get(), 8192);
3727 nErr = pImpl->m_pInStream->GetError();
3728 pImpl->m_pOutStream->WriteBytes( pBuf.get(), nRead );
3731 bTransferSuccess = true;
3732 CloseInStream();
3734 CloseOutStream_Impl();
3736 else
3738 // Quite strange design, but currently it is expected that in this case no transfer happens
3739 // TODO/LATER: get rid of this inconsistent part of the call design
3740 bTransferSuccess = true;
3741 CloseInStream();
3744 if ( !bTransferSuccess )
3746 SetError(ERRCODE_IO_CANTWRITE);
3747 return;
3751 CloseStorage();
3755 void SfxMedium::CreateTempFileNoCopy()
3757 // this call always replaces the existing temporary file
3758 pImpl->pTempFile.reset();
3760 OUString aLogicBase = GetLogicBase(GetURLObject(), pImpl);
3761 pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) );
3762 pImpl->pTempFile->EnableKillingFile();
3763 pImpl->m_aName = pImpl->pTempFile->GetFileName();
3764 if ( pImpl->m_aName.isEmpty() )
3766 SetError(ERRCODE_IO_CANTWRITE);
3767 return;
3770 CloseOutStream_Impl();
3771 CloseStorage();
3774 bool SfxMedium::SignDocumentContentUsingCertificate(
3775 const css::uno::Reference<css::frame::XModel>& xModel, bool bHasValidDocumentSignature,
3776 const Reference<XCertificate>& xCertificate)
3778 bool bChanges = false;
3780 if (IsOpen() || GetError())
3782 SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
3783 return bChanges;
3786 // The component should know if there was a valid document signature, since
3787 // it should show a warning in this case
3788 OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
3789 uno::Reference< security::XDocumentDigitalSignatures > xSigner(
3790 security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
3791 comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
3792 auto xModelSigner = dynamic_cast<sfx2::DigitalSignatures*>(xSigner.get());
3793 if (!xModelSigner)
3795 return bChanges;
3798 uno::Reference< embed::XStorage > xWriteableZipStor;
3800 // we can reuse the temporary file if there is one already
3801 CreateTempFile( false );
3802 GetMedium_Impl();
3806 if ( !pImpl->xStream.is() )
3807 throw uno::RuntimeException();
3809 bool bODF = GetFilter()->IsOwnFormat();
3812 xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
3814 catch (const io::IOException&)
3816 if (bODF)
3818 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
3822 if ( !xWriteableZipStor.is() && bODF )
3823 throw uno::RuntimeException();
3825 uno::Reference< embed::XStorage > xMetaInf;
3826 if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
3828 xMetaInf = xWriteableZipStor->openStorageElement(
3829 "META-INF",
3830 embed::ElementModes::READWRITE );
3831 if ( !xMetaInf.is() )
3832 throw uno::RuntimeException();
3836 if (xMetaInf.is())
3838 // ODF.
3839 uno::Reference< io::XStream > xStream;
3840 if (GetFilter() && GetFilter()->IsOwnFormat())
3841 xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
3843 bool bSuccess = xModelSigner->SignModelWithCertificate(
3844 xModel, xCertificate, GetZipStorageToSign_Impl(), xStream);
3846 if (bSuccess)
3848 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
3849 xTransact->commit();
3850 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
3851 xTransact->commit();
3853 // the temporary file has been written, commit it to the original file
3854 Commit();
3855 bChanges = true;
3858 else if (xWriteableZipStor.is())
3860 // OOXML.
3861 uno::Reference<io::XStream> xStream;
3863 // We need read-write to be able to add the signature relation.
3864 bool bSuccess = xModelSigner->SignModelWithCertificate(
3865 xModel, xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
3867 if (bSuccess)
3869 uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
3870 xTransact->commit();
3872 // the temporary file has been written, commit it to the original file
3873 Commit();
3874 bChanges = true;
3877 else
3879 // Something not ZIP based: e.g. PDF.
3880 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
3881 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
3882 if (xModelSigner->SignModelWithCertificate(
3883 xModel, xCertificate, uno::Reference<embed::XStorage>(), xStream))
3884 bChanges = true;
3888 catch ( const uno::Exception& )
3890 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
3893 CloseAndRelease();
3895 ResetError();
3897 return bChanges;
3900 bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent,
3901 bool bSignScriptingContent,
3902 bool bHasValidDocumentSignature,
3903 const OUString& aSignatureLineId,
3904 const Reference<XCertificate>& xCert,
3905 const Reference<XGraphic>& xValidGraphic,
3906 const Reference<XGraphic>& xInvalidGraphic,
3907 const OUString& aComment)
3909 bool bChanges = false;
3911 if (IsOpen() || GetError())
3913 SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
3914 return bChanges;
3917 // The component should know if there was a valid document signature, since
3918 // it should show a warning in this case
3919 OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
3920 uno::Reference< security::XDocumentDigitalSignatures > xSigner(
3921 security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
3922 comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
3923 if (pDialogParent)
3924 xSigner->setParentWindow(pDialogParent->GetXWindow());
3926 uno::Reference< embed::XStorage > xWriteableZipStor;
3928 // we can reuse the temporary file if there is one already
3929 CreateTempFile( false );
3930 GetMedium_Impl();
3934 if ( !pImpl->xStream.is() )
3935 throw uno::RuntimeException();
3937 bool bODF = GetFilter()->IsOwnFormat();
3940 xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
3942 catch (const io::IOException&)
3944 if (bODF)
3946 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
3950 if ( !xWriteableZipStor.is() && bODF )
3951 throw uno::RuntimeException();
3953 uno::Reference< embed::XStorage > xMetaInf;
3954 if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
3956 xMetaInf = xWriteableZipStor->openStorageElement(
3957 "META-INF",
3958 embed::ElementModes::READWRITE );
3959 if ( !xMetaInf.is() )
3960 throw uno::RuntimeException();
3963 if ( bSignScriptingContent )
3965 // If the signature has already the document signature it will be removed
3966 // after the scripting signature is inserted.
3967 uno::Reference< io::XStream > xStream(
3968 xMetaInf->openStreamElement( xSigner->getScriptingContentSignatureDefaultStreamName(),
3969 embed::ElementModes::READWRITE ),
3970 uno::UNO_SET_THROW );
3972 if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), xStream ) )
3974 // remove the document signature if any
3975 OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName();
3976 if ( !aDocSigName.isEmpty() && xMetaInf->hasByName( aDocSigName ) )
3977 xMetaInf->removeElement( aDocSigName );
3979 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
3980 xTransact->commit();
3981 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
3982 xTransact->commit();
3984 // the temporary file has been written, commit it to the original file
3985 Commit();
3986 bChanges = true;
3989 else
3991 if (xMetaInf.is())
3993 // ODF.
3994 uno::Reference< io::XStream > xStream;
3995 if (GetFilter() && GetFilter()->IsOwnFormat())
3996 xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
3998 bool bSuccess = false;
3999 if (xCert.is())
4000 bSuccess = xSigner->signSignatureLine(
4001 GetZipStorageToSign_Impl(), xStream, aSignatureLineId, xCert,
4002 xValidGraphic, xInvalidGraphic, aComment);
4003 else
4004 bSuccess = xSigner->signDocumentContent(GetZipStorageToSign_Impl(),
4005 xStream);
4007 if (bSuccess)
4009 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
4010 xTransact->commit();
4011 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
4012 xTransact->commit();
4014 // the temporary file has been written, commit it to the original file
4015 Commit();
4016 bChanges = true;
4019 else if (xWriteableZipStor.is())
4021 // OOXML.
4022 uno::Reference<io::XStream> xStream;
4024 bool bSuccess = false;
4025 if (xCert.is())
4027 bSuccess = xSigner->signSignatureLine(
4028 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream, aSignatureLineId,
4029 xCert, xValidGraphic, xInvalidGraphic, aComment);
4031 else
4033 // We need read-write to be able to add the signature relation.
4034 bSuccess =xSigner->signDocumentContent(
4035 GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
4038 if (bSuccess)
4040 uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
4041 xTransact->commit();
4043 // the temporary file has been written, commit it to the original file
4044 Commit();
4045 bChanges = true;
4048 else
4050 // Something not ZIP based: e.g. PDF.
4051 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
4052 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
4053 if (xSigner->signDocumentContent(uno::Reference<embed::XStorage>(), xStream))
4054 bChanges = true;
4058 catch ( const uno::Exception& )
4060 SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
4063 CloseAndRelease();
4065 ResetError();
4067 return bChanges;
4071 SignatureState SfxMedium::GetCachedSignatureState_Impl() const
4073 return pImpl->m_nSignatureState;
4077 void SfxMedium::SetCachedSignatureState_Impl( SignatureState nState )
4079 pImpl->m_nSignatureState = nState;
4082 void SfxMedium::SetHasEmbeddedObjects(bool bHasEmbeddedObjects)
4084 pImpl->m_bHasEmbeddedObjects = bHasEmbeddedObjects;
4087 bool SfxMedium::HasStorage_Impl() const
4089 return pImpl->xStorage.is();
4092 bool SfxMedium::IsOpen() const
4094 return pImpl->m_pInStream || pImpl->m_pOutStream || pImpl->xStorage.is();
4097 OUString SfxMedium::CreateTempCopyWithExt( const OUString& aURL )
4099 OUString aResult;
4101 if ( !aURL.isEmpty() )
4103 sal_Int32 nPrefixLen = aURL.lastIndexOf( '.' );
4104 OUString aExt = ( nPrefixLen == -1 ) ? OUString() : aURL.copy( nPrefixLen );
4106 OUString aNewTempFileURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
4107 if ( !aNewTempFileURL.isEmpty() )
4109 INetURLObject aSource( aURL );
4110 INetURLObject aDest( aNewTempFileURL );
4111 OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT,
4112 true,
4113 INetURLObject::DecodeMechanism::WithCharset );
4114 if ( !aFileName.isEmpty() && aDest.removeSegment() )
4118 uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
4119 ::ucbhelper::Content aTargetContent( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
4120 ::ucbhelper::Content aSourceContent( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
4121 aTargetContent.transferContent( aSourceContent,
4122 ::ucbhelper::InsertOperation::Copy,
4123 aFileName,
4124 NameClash::OVERWRITE );
4125 aResult = aNewTempFileURL;
4127 catch( const uno::Exception& )
4133 return aResult;
4136 bool SfxMedium::CallApproveHandler(const uno::Reference< task::XInteractionHandler >& xHandler, const uno::Any& rRequest, bool bAllowAbort)
4138 bool bResult = false;
4140 if ( xHandler.is() )
4144 uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( bAllowAbort ? 2 : 1 );
4146 ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
4147 aContinuations[ 0 ] = pApprove.get();
4149 if ( bAllowAbort )
4151 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( new ::comphelper::OInteractionAbort );
4152 aContinuations[ 1 ] = pAbort.get();
4155 xHandler->handle(::framework::InteractionRequest::CreateRequest(rRequest, aContinuations));
4156 bResult = pApprove->wasSelected();
4158 catch( const Exception& )
4163 return bResult;
4166 OUString SfxMedium::SwitchDocumentToTempFile()
4168 // the method returns empty string in case of failure
4169 OUString aResult;
4170 OUString aOrigURL = pImpl->m_aLogicName;
4172 if ( !aOrigURL.isEmpty() )
4174 sal_Int32 nPrefixLen = aOrigURL.lastIndexOf( '.' );
4175 OUString const aExt = (nPrefixLen == -1)
4176 ? OUString()
4177 : aOrigURL.copy(nPrefixLen);
4178 OUString aNewURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
4180 // TODO/LATER: In future the aLogicName should be set to shared folder URL
4181 // and a temporary file should be created. Transport_Impl should be impossible then.
4182 if ( !aNewURL.isEmpty() )
4184 uno::Reference< embed::XStorage > xStorage = GetStorage();
4185 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
4187 if ( xOptStorage.is() )
4189 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4190 CanDisposeStorage_Impl( false );
4191 Close();
4192 SetPhysicalName_Impl( OUString() );
4193 SetName( aNewURL );
4195 // remove the readonly state
4196 bool bWasReadonly = false;
4197 pImpl->m_nStorOpenMode = SFX_STREAM_READWRITE;
4198 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
4199 if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
4200 bWasReadonly = true;
4201 GetItemSet()->ClearItem( SID_DOC_READONLY );
4203 GetMedium_Impl();
4204 LockOrigFileOnDemand( false, false );
4205 CreateTempFile();
4206 GetMedium_Impl();
4208 if ( pImpl->xStream.is() )
4212 xOptStorage->writeAndAttachToStream( pImpl->xStream );
4213 pImpl->xStorage = xStorage;
4214 aResult = aNewURL;
4216 catch( const uno::Exception& )
4220 if ( aResult.isEmpty() )
4222 Close();
4223 SetPhysicalName_Impl( OUString() );
4224 SetName( aOrigURL );
4225 if ( bWasReadonly )
4227 // set the readonly state back
4228 pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
4229 GetItemSet()->Put( SfxBoolItem(SID_DOC_READONLY, true));
4231 GetMedium_Impl();
4232 pImpl->xStorage = xStorage;
4238 return aResult;
4241 bool SfxMedium::SwitchDocumentToFile( const OUString& aURL )
4243 // the method is only for storage based documents
4244 bool bResult = false;
4245 OUString aOrigURL = pImpl->m_aLogicName;
4247 if ( !aURL.isEmpty() && !aOrigURL.isEmpty() )
4249 uno::Reference< embed::XStorage > xStorage = GetStorage();
4250 uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
4252 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4253 CanDisposeStorage_Impl( false );
4254 Close();
4255 SetPhysicalName_Impl( OUString() );
4256 SetName( aURL );
4258 // open the temporary file based document
4259 GetMedium_Impl();
4260 LockOrigFileOnDemand( false, false );
4261 CreateTempFile();
4262 GetMedium_Impl();
4264 if ( pImpl->xStream.is() )
4268 uno::Reference< io::XTruncate > xTruncate( pImpl->xStream, uno::UNO_QUERY_THROW );
4269 xTruncate->truncate();
4270 if ( xOptStorage.is() )
4271 xOptStorage->writeAndAttachToStream( pImpl->xStream );
4272 pImpl->xStorage = xStorage;
4273 bResult = true;
4275 catch( const uno::Exception& )
4279 if ( !bResult )
4281 Close();
4282 SetPhysicalName_Impl( OUString() );
4283 SetName( aOrigURL );
4284 GetMedium_Impl();
4285 pImpl->xStorage = xStorage;
4289 return bResult;
4292 void SfxMedium::SetInCheckIn( bool bInCheckIn )
4294 pImpl->m_bInCheckIn = bInCheckIn;
4297 bool SfxMedium::IsInCheckIn( ) const
4299 return pImpl->m_bInCheckIn;
4302 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */