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