xref: /core/sfx2/source/doc/docfile.cxx (revision 9aadd633)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_features.h>
21 
22 #ifdef UNX
23 #include <sys/stat.h>
24 #endif
25 
26 #include <sfx2/docfile.hxx>
27 #include <sfx2/signaturestate.hxx>
28 
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/InteractiveNetworkWriteException.hpp>
55 #include <com/sun/star/ucb/Lock.hpp>
56 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
57 #include <com/sun/star/ucb/XContentIdentifierFactory.hpp>
58 #include <com/sun/star/ucb/XContentProvider.hpp>
59 #include <com/sun/star/ucb/XProgressHandler.hpp>
60 #include <com/sun/star/ucb/XCommandInfo.hpp>
61 #include <com/sun/star/io/XOutputStream.hpp>
62 #include <com/sun/star/io/XInputStream.hpp>
63 #include <com/sun/star/io/XTruncate.hpp>
64 #include <com/sun/star/io/XStreamListener.hpp>
65 #include <com/sun/star/io/XSeekable.hpp>
66 #include <com/sun/star/ucb/XSimpleFileAccess.hpp>
67 #include <com/sun/star/lang/XInitialization.hpp>
68 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
69 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
70 #include <com/sun/star/ucb/NameClash.hpp>
71 #include <com/sun/star/ucb/TransferInfo.hpp>
72 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
73 #include <com/sun/star/ucb/OpenMode.hpp>
74 #include <com/sun/star/beans/NamedValue.hpp>
75 #include <com/sun/star/beans/PropertyValue.hpp>
76 #include <com/sun/star/security/DocumentSignatureInformation.hpp>
77 #include <com/sun/star/security/DocumentDigitalSignatures.hpp>
78 #include <com/sun/star/security/XCertificate.hpp>
79 #include <tools/urlobj.hxx>
80 #include <tools/fileutil.hxx>
81 #include <unotools/configmgr.hxx>
82 #include <unotools/tempfile.hxx>
83 #include <comphelper/fileurl.hxx>
84 #include <comphelper/processfactory.hxx>
85 #include <comphelper/interaction.hxx>
86 #include <comphelper/sequence.hxx>
87 #include <comphelper/simplefileaccessinteraction.hxx>
88 #include <framework/interaction.hxx>
89 #include <unotools/streamhelper.hxx>
90 #include <unotools/localedatawrapper.hxx>
91 #include <utility>
92 #include <svl/stritem.hxx>
93 #include <svl/eitem.hxx>
94 #include <svl/lckbitem.hxx>
95 #include <svtools/sfxecode.hxx>
96 #include <svl/itemset.hxx>
97 #include <svl/intitem.hxx>
98 #include <svtools/svparser.hxx>
99 #include <cppuhelper/weakref.hxx>
100 #include <sal/log.hxx>
101 
102 #include <unotools/streamwrap.hxx>
103 
104 #include <osl/file.hxx>
105 
106 #include <comphelper/storagehelper.hxx>
107 #include <unotools/mediadescriptor.hxx>
108 #include <comphelper/docpasswordhelper.hxx>
109 #include <tools/datetime.hxx>
110 #include <unotools/pathoptions.hxx>
111 #include <svtools/asynclink.hxx>
112 #include <ucbhelper/commandenvironment.hxx>
113 #include <unotools/ucbstreamhelper.hxx>
114 #include <unotools/ucbhelper.hxx>
115 #include <unotools/progresshandlerwrap.hxx>
116 #include <ucbhelper/content.hxx>
117 #include <ucbhelper/interactionrequest.hxx>
118 #include <sot/stg.hxx>
119 #include <sot/storage.hxx>
120 #include <unotools/saveopt.hxx>
121 #include <svl/documentlockfile.hxx>
122 #include <svl/msodocumentlockfile.hxx>
123 #include <com/sun/star/document/DocumentRevisionListPersistence.hpp>
124 
125 #include <helper.hxx>
126 #include <sfx2/request.hxx>
127 #include <sfx2/app.hxx>
128 #include <sfx2/frame.hxx>
129 #include <sfx2/fcontnr.hxx>
130 #include <sfx2/docfilt.hxx>
131 #include <sfx2/objsh.hxx>
132 #include <sfx2/docfac.hxx>
133 #include <sfx2/sfxsids.hrc>
134 #include <sfx2/sfxuno.hxx>
135 #include <openflag.hxx>
136 #include <officecfg/Office/Common.hxx>
137 #include <comphelper/propertysequence.hxx>
138 #include <vcl/weld.hxx>
139 #include <vcl/svapp.hxx>
140 #include <tools/diagnose_ex.h>
141 #include <unotools/fltrcfg.hxx>
142 
143 #include <com/sun/star/io/WrongFormatException.hpp>
144 
145 #include <memory>
146 
147 using namespace ::com::sun::star;
148 using namespace ::com::sun::star::graphic;
149 using namespace ::com::sun::star::uno;
150 using namespace ::com::sun::star::ucb;
151 using namespace ::com::sun::star::beans;
152 using namespace ::com::sun::star::io;
153 using namespace ::com::sun::star::security;
154 
155 namespace {
156 
157 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
158 
159 bool IsSystemFileLockingUsed()
160 {
161 #if HAVE_FEATURE_MACOSX_SANDBOX
162     return true;
163 #else
164     return officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
165 #endif
166 }
167 
168 
169 bool IsOOoLockFileUsed()
170 {
171 #if HAVE_FEATURE_MACOSX_SANDBOX
172     return false;
173 #else
174     return officecfg::Office::Common::Misc::UseDocumentOOoLockFile::get();
175 #endif
176 }
177 
178 bool IsLockingUsed()
179 {
180     return officecfg::Office::Common::Misc::UseLocking::get();
181 }
182 
183 #endif
184 
185 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
186 bool IsWebDAVLockingUsed()
187 {
188     return officecfg::Office::Common::Misc::UseWebDAVFileLocking::get();
189 }
190 #endif
191 
192 /// Gets default attributes of a file:// URL.
193 sal_uInt64 GetDefaultFileAttributes(const OUString& rURL)
194 {
195     sal_uInt64 nRet = 0;
196 
197     if (!comphelper::isFileUrl(rURL))
198         return nRet;
199 
200     // Make sure the file exists (and create it if not).
201     osl::File aFile(rURL);
202     osl::File::RC nRes = aFile.open(osl_File_OpenFlag_Create);
203     if (nRes != osl::File::E_None && nRes != osl::File::E_EXIST)
204         return nRet;
205 
206     aFile.close();
207 
208     osl::DirectoryItem aItem;
209     if (osl::DirectoryItem::get(rURL, aItem) != osl::DirectoryItem::E_None)
210         return nRet;
211 
212     osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
213     if (aItem.getFileStatus(aStatus) != osl::DirectoryItem::E_None)
214         return nRet;
215 
216     nRet = aStatus.getAttributes();
217     return nRet;
218 }
219 
220 /// Determines if rURL is safe to move or not.
221 bool IsFileMovable(const INetURLObject& rURL)
222 {
223 #ifdef MACOSX
224     (void)rURL;
225     // Hide extension macOS-specific file property would be lost.
226     return false;
227 #else
228 
229     if (rURL.GetProtocol() != INetProtocol::File)
230         // Not a file:// URL.
231         return false;
232 
233 #ifdef UNX
234     OUString sPath = rURL.getFSysPath(FSysStyle::Unix);
235     if (sPath.isEmpty())
236         return false;
237 
238     struct stat buf;
239     if (lstat(sPath.toUtf8().getStr(), &buf) != 0)
240         return false;
241 
242     // Hardlink or symlink: osl::File::move() doesn't play with these nicely.
243     if (buf.st_nlink > 1 || S_ISLNK(buf.st_mode))
244         return false;
245 #elif defined _WIN32
246     if (tools::IsMappedWebDAVPath(rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)))
247         return false;
248 #endif
249 
250     return true;
251 #endif
252 }
253 
254 } // anonymous namespace
255 
256 class SfxMedium_Impl
257 {
258 public:
259     StreamMode m_nStorOpenMode;
260     ErrCode    m_eError;
261 
262     ::ucbhelper::Content aContent;
263     bool bUpdatePickList:1;
264     bool bIsTemp:1;
265     bool bDownloadDone:1;
266     bool bIsStorage:1;
267     bool bUseInteractionHandler:1;
268     bool bAllowDefaultIntHdl:1;
269     bool bDisposeStorage:1;
270     bool bStorageBasedOnInStream:1;
271     bool m_bSalvageMode:1;
272     bool m_bVersionsAlreadyLoaded:1;
273     bool m_bLocked:1;
274     bool m_bMSOLockFileCreated : 1;
275     bool m_bDisableUnlockWebDAV:1;
276     bool m_bGotDateTime:1;
277     bool m_bRemoveBackup:1;
278     bool m_bOriginallyReadOnly:1;
279     bool m_bOriginallyLoadedReadOnly:1;
280     bool m_bTriedStorage:1;
281     bool m_bRemote:1;
282     bool m_bInputStreamIsReadOnly:1;
283     bool m_bInCheckIn:1;
284     bool m_bDisableFileSync = false;
285 
286     OUString m_aName;
287     OUString m_aLogicName;
288     OUString m_aLongName;
289 
290     mutable std::unique_ptr<SfxItemSet> m_pSet;
291     mutable std::unique_ptr<INetURLObject> m_pURLObj;
292 
293     std::shared_ptr<const SfxFilter> m_pFilter;
294     std::shared_ptr<const SfxFilter> m_pCustomFilter;
295 
296     std::unique_ptr<SvStream> m_pInStream;
297     std::unique_ptr<SvStream> m_pOutStream;
298 
299     OUString    aOrigURL;
300     DateTime         aExpireTime;
301     SfxFrameWeakRef  wLoadTargetFrame;
302     SvKeyValueIteratorRef xAttributes;
303 
304     svtools::AsynchronLink  aDoneLink;
305 
306     uno::Sequence < util::RevisionTag > aVersions;
307 
308     std::unique_ptr<::utl::TempFile> pTempFile;
309 
310     uno::Reference<embed::XStorage> xStorage;
311     uno::Reference<embed::XStorage> m_xZipStorage;
312     uno::Reference<io::XInputStream> m_xInputStreamToLoadFrom;
313     uno::Reference<io::XInputStream> xInputStream;
314     uno::Reference<io::XStream> xStream;
315     uno::Reference<io::XStream> m_xLockingStream;
316     uno::Reference<task::XInteractionHandler> xInteraction;
317 
318     ErrCode  nLastStorageError;
319 
320     OUString m_aBackupURL;
321 
322     // the following member is changed and makes sense only during saving
323     // TODO/LATER: in future the signature state should be controlled by the medium not by the document
324     //             in this case the member will hold this information
325     SignatureState             m_nSignatureState;
326 
327     bool m_bHasEmbeddedObjects = false;
328 
329     util::DateTime m_aDateTime;
330 
331     explicit SfxMedium_Impl();
332     ~SfxMedium_Impl();
333     SfxMedium_Impl(const SfxMedium_Impl&) = delete;
334     SfxMedium_Impl& operator=(const SfxMedium_Impl&) = delete;
335 
336     OUString getFilterMimeType() const
337         { return !m_pFilter ? OUString() : m_pFilter->GetMimeType(); }
338 };
339 
340 
341 SfxMedium_Impl::SfxMedium_Impl() :
342     m_nStorOpenMode(SFX_STREAM_READWRITE),
343     m_eError(ERRCODE_NONE),
344     bUpdatePickList(true),
345     bIsTemp( false ),
346     bDownloadDone( true ),
347     bIsStorage( false ),
348     bUseInteractionHandler( true ),
349     bAllowDefaultIntHdl( false ),
350     bDisposeStorage( false ),
351     bStorageBasedOnInStream( false ),
352     m_bSalvageMode( false ),
353     m_bVersionsAlreadyLoaded( false ),
354     m_bLocked( false ),
355     m_bMSOLockFileCreated( false ),
356     m_bDisableUnlockWebDAV( false ),
357     m_bGotDateTime( false ),
358     m_bRemoveBackup( false ),
359     m_bOriginallyReadOnly(false),
360     m_bOriginallyLoadedReadOnly(false),
361     m_bTriedStorage(false),
362     m_bRemote(false),
363     m_bInputStreamIsReadOnly(false),
364     m_bInCheckIn(false),
365     aExpireTime( DateTime( DateTime::SYSTEM ) + static_cast<sal_Int32>(10) ),
366     nLastStorageError( ERRCODE_NONE ),
367     m_nSignatureState( SignatureState::NOSIGNATURES )
368 {
369     aDoneLink.CreateMutex();
370 }
371 
372 
373 SfxMedium_Impl::~SfxMedium_Impl()
374 {
375     aDoneLink.ClearPendingCall();
376 
377     pTempFile.reset();
378     m_pSet.reset();
379     m_pURLObj.reset();
380 }
381 
382 void SfxMedium::ResetError()
383 {
384     pImpl->m_eError = ERRCODE_NONE;
385     if( pImpl->m_pInStream )
386         pImpl->m_pInStream->ResetError();
387     if( pImpl->m_pOutStream )
388         pImpl->m_pOutStream->ResetError();
389 }
390 
391 ErrCode const & SfxMedium::GetLastStorageCreationState() const
392 {
393     return pImpl->nLastStorageError;
394 }
395 
396 void SfxMedium::SetError(ErrCode nError)
397 {
398     pImpl->m_eError = nError;
399 }
400 
401 ErrCode SfxMedium::GetErrorCode() const
402 {
403     ErrCode lError = pImpl->m_eError;
404     if(!lError && pImpl->m_pInStream)
405         lError = pImpl->m_pInStream->GetErrorCode();
406     if(!lError && pImpl->m_pOutStream)
407         lError = pImpl->m_pOutStream->GetErrorCode();
408     return lError;
409 }
410 
411 void SfxMedium::CheckFileDate( const util::DateTime& aInitDate )
412 {
413     GetInitFileDate( true );
414     if ( pImpl->m_aDateTime.Seconds == aInitDate.Seconds
415       && pImpl->m_aDateTime.Minutes == aInitDate.Minutes
416       && pImpl->m_aDateTime.Hours == aInitDate.Hours
417       && pImpl->m_aDateTime.Day == aInitDate.Day
418       && pImpl->m_aDateTime.Month == aInitDate.Month
419       && pImpl->m_aDateTime.Year == aInitDate.Year )
420         return;
421 
422     uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
423 
424     if ( !xHandler.is() )
425         return;
426 
427     try
428     {
429         ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
430             document::ChangedByOthersRequest() ) );
431         uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( 3 );
432         aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
433         aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
434         xInteractionRequestImpl->setContinuations( aContinuations );
435 
436         xHandler->handle( xInteractionRequestImpl.get() );
437 
438         ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
439         if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
440         {
441             SetError(ERRCODE_ABORT);
442         }
443     }
444     catch ( const uno::Exception& )
445     {}
446 }
447 
448 bool SfxMedium::DocNeedsFileDateCheck() const
449 {
450     return ( !IsReadOnly() && ( GetURLObject().GetProtocol() == INetProtocol::File ||
451                                 GetURLObject().isAnyKnownWebDAVScheme() ) );
452 }
453 
454 util::DateTime const & SfxMedium::GetInitFileDate( bool bIgnoreOldValue )
455 {
456     if ( ( bIgnoreOldValue || !pImpl->m_bGotDateTime ) && !pImpl->m_aLogicName.isEmpty() )
457     {
458         try
459         {
460             // add a default css::ucb::XCommandEnvironment
461             // in order to have the WebDAV UCP provider manage http/https authentication correctly
462             ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
463                                            utl::UCBContentHelper::getDefaultCommandEnvironment(),
464                                            comphelper::getProcessComponentContext() );
465 
466             aContent.getPropertyValue("DateModified") >>= pImpl->m_aDateTime;
467             pImpl->m_bGotDateTime = true;
468         }
469         catch ( const css::uno::Exception& )
470         {
471         }
472     }
473 
474     return pImpl->m_aDateTime;
475 }
476 
477 
478 Reference < XContent > SfxMedium::GetContent() const
479 {
480     if ( !pImpl->aContent.get().is() )
481     {
482         Reference < css::ucb::XContent > xContent;
483 
484         // tdf#95144 add a default css::ucb::XCommandEnvironment
485         // in order to have the WebDAV UCP provider manage https protocol certificates correctly
486         css:: uno::Reference< task::XInteractionHandler > xIH(
487                 css::task::InteractionHandler::createWithParent( comphelper::getProcessComponentContext(), nullptr ) );
488 
489         css::uno::Reference< css::ucb::XProgressHandler > xProgress;
490         ::ucbhelper::CommandEnvironment* pCommandEnv = new ::ucbhelper::CommandEnvironment( new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
491 
492         Reference < css::ucb::XCommandEnvironment > xEnv( static_cast< css::ucb::XCommandEnvironment* >(pCommandEnv), css::uno::UNO_QUERY );
493 
494         const SfxUnoAnyItem* pItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_CONTENT, false);
495         if ( pItem )
496             pItem->GetValue() >>= xContent;
497 
498         if ( xContent.is() )
499         {
500             try
501             {
502                 pImpl->aContent = ::ucbhelper::Content( xContent, xEnv, comphelper::getProcessComponentContext() );
503             }
504             catch ( const Exception& )
505             {
506             }
507         }
508         else
509         {
510             // TODO: SAL_WARN( "sfx.doc", "SfxMedium::GetContent()\nCreate Content? This code exists as fallback only. Please clarify, why it's used.");
511             OUString aURL;
512             if ( !pImpl->m_aName.isEmpty() )
513                 osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL );
514             else if ( !pImpl->m_aLogicName.isEmpty() )
515                 aURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
516             if (!aURL.isEmpty() )
517                 (void)::ucbhelper::Content::create( aURL, xEnv, comphelper::getProcessComponentContext(), pImpl->aContent );
518         }
519     }
520 
521     return pImpl->aContent.get();
522 }
523 
524 OUString SfxMedium::GetBaseURL( bool bForSaving )
525 {
526     OUString aBaseURL;
527     const SfxStringItem* pBaseURLItem = GetItemSet()->GetItem<SfxStringItem>(SID_DOC_BASEURL);
528     if ( pBaseURLItem )
529         aBaseURL = pBaseURLItem->GetValue();
530     else if (!utl::ConfigManager::IsFuzzing() && GetContent().is())
531     {
532         try
533         {
534             Any aAny = pImpl->aContent.getPropertyValue("BaseURI");
535             aAny >>= aBaseURL;
536         }
537         catch ( const css::uno::Exception& )
538         {
539         }
540 
541         if ( aBaseURL.isEmpty() )
542             aBaseURL = GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE );
543     }
544 
545     if ( bForSaving )
546     {
547         SvtSaveOptions aOpt;
548         bool bIsRemote = IsRemote();
549         if( (bIsRemote && !aOpt.IsSaveRelINet()) || (!pImpl->m_bRemote && !aOpt.IsSaveRelFSys()) )
550             return OUString();
551     }
552 
553     return aBaseURL;
554 }
555 
556 bool SfxMedium::IsSkipImages() const
557 {
558     const SfxStringItem* pSkipImagesItem = GetItemSet()->GetItem<SfxStringItem>(SID_FILE_FILTEROPTIONS);
559     return pSkipImagesItem && pSkipImagesItem->GetValue() == "SkipImages";
560 }
561 
562 SvStream* SfxMedium::GetInStream()
563 {
564     if ( pImpl->m_pInStream )
565         return pImpl->m_pInStream.get();
566 
567     if ( pImpl->pTempFile )
568     {
569         pImpl->m_pInStream.reset( new SvFileStream(pImpl->m_aName, pImpl->m_nStorOpenMode) );
570 
571         pImpl->m_eError = pImpl->m_pInStream->GetError();
572 
573         if (!pImpl->m_eError && (pImpl->m_nStorOpenMode & StreamMode::WRITE)
574                     && ! pImpl->m_pInStream->IsWritable() )
575         {
576             pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
577             pImpl->m_pInStream.reset();
578         }
579         else
580             return pImpl->m_pInStream.get();
581     }
582 
583     GetMedium_Impl();
584 
585     if ( GetError() )
586         return nullptr;
587 
588     return pImpl->m_pInStream.get();
589 }
590 
591 
592 void SfxMedium::CloseInStream()
593 {
594     CloseInStream_Impl();
595 }
596 
597 void SfxMedium::CloseInStream_Impl(bool bInDestruction)
598 {
599     // if there is a storage based on the InStream, we have to
600     // close the storage, too, because otherwise the storage
601     // would use an invalid ( deleted ) stream.
602     if ( pImpl->m_pInStream && pImpl->xStorage.is() )
603     {
604         if ( pImpl->bStorageBasedOnInStream )
605             CloseStorage();
606     }
607 
608     if ( pImpl->m_pInStream && !GetContent().is() && !bInDestruction )
609     {
610         CreateTempFile();
611         return;
612     }
613 
614     pImpl->m_pInStream.reset();
615     if ( pImpl->m_pSet )
616         pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
617 
618     CloseZipStorage_Impl();
619     pImpl->xInputStream.clear();
620 
621     if ( !pImpl->m_pOutStream )
622     {
623         // output part of the stream is not used so the whole stream can be closed
624         // TODO/LATER: is it correct?
625         pImpl->xStream.clear();
626         if ( pImpl->m_pSet )
627             pImpl->m_pSet->ClearItem( SID_STREAM );
628     }
629 }
630 
631 
632 SvStream* SfxMedium::GetOutStream()
633 {
634     if ( !pImpl->m_pOutStream )
635     {
636         // Create a temp. file if there is none because we always
637         // need one.
638         CreateTempFile( false );
639 
640         if ( pImpl->pTempFile )
641         {
642             // On windows we try to re-use XOutStream from xStream if that exists;
643             // because opening new SvFileStream in this situation may fail with ERROR_SHARING_VIOLATION
644             // TODO: this is a horrible hack that should probably be removed,
645             // somebody needs to investigate this more thoroughly...
646             if (getenv("SFX_MEDIUM_REUSE_STREAM") && pImpl->xStream.is())
647             {
648                 assert(pImpl->xStream->getOutputStream().is()); // need that...
649                 pImpl->m_pOutStream = utl::UcbStreamHelper::CreateStream(
650                         pImpl->xStream, false);
651             }
652             else
653             {
654             // On Unix don't try to re-use XOutStream from xStream if that exists;
655             // it causes fdo#59022 (fails opening files via SMB on Linux)
656                 pImpl->m_pOutStream.reset( new SvFileStream(
657                             pImpl->m_aName, StreamMode::STD_READWRITE) );
658             }
659             CloseStorage();
660         }
661     }
662 
663     return pImpl->m_pOutStream.get();
664 }
665 
666 
667 void SfxMedium::CloseOutStream()
668 {
669     CloseOutStream_Impl();
670 }
671 
672 void SfxMedium::CloseOutStream_Impl()
673 {
674     if ( pImpl->m_pOutStream )
675     {
676         // if there is a storage based on the OutStream, we have to
677         // close the storage, too, because otherwise the storage
678         // would use an invalid ( deleted ) stream.
679         //TODO/MBA: how to deal with this?!
680         //maybe we need a new flag when the storage was created from the outstream
681         if ( pImpl->xStorage.is() )
682         {
683                 CloseStorage();
684         }
685 
686         pImpl->m_pOutStream.reset();
687     }
688 
689     if ( !pImpl->m_pInStream )
690     {
691         // input part of the stream is not used so the whole stream can be closed
692         // TODO/LATER: is it correct?
693         pImpl->xStream.clear();
694         if ( pImpl->m_pSet )
695             pImpl->m_pSet->ClearItem( SID_STREAM );
696     }
697 }
698 
699 
700 const OUString& SfxMedium::GetPhysicalName() const
701 {
702     if ( pImpl->m_aName.isEmpty() && !pImpl->m_aLogicName.isEmpty() )
703         const_cast<SfxMedium*>(this)->CreateFileStream();
704 
705     // return the name then
706     return pImpl->m_aName;
707 }
708 
709 
710 void SfxMedium::CreateFileStream()
711 {
712     // force synchron
713     if( pImpl->m_pInStream )
714     {
715         SvLockBytes* pBytes = pImpl->m_pInStream->GetLockBytes();
716         if( pBytes )
717             pBytes->SetSynchronMode();
718     }
719 
720     GetInStream();
721     if( pImpl->m_pInStream )
722     {
723         CreateTempFile( false );
724         pImpl->bIsTemp = true;
725         CloseInStream_Impl();
726     }
727 }
728 
729 
730 bool SfxMedium::Commit()
731 {
732     if( pImpl->xStorage.is() )
733         StorageCommit_Impl();
734     else if( pImpl->m_pOutStream  )
735         pImpl->m_pOutStream->Flush();
736     else if( pImpl->m_pInStream  )
737         pImpl->m_pInStream->Flush();
738 
739     if ( GetError() == ERRCODE_NONE )
740     {
741         // does something only in case there is a temporary file ( means aName points to different location than aLogicName )
742         Transfer_Impl();
743     }
744 
745     bool bResult = ( GetError() == ERRCODE_NONE );
746 
747     if ( bResult && DocNeedsFileDateCheck() )
748         GetInitFileDate( true );
749 
750     // remove truncation mode from the flags
751     pImpl->m_nStorOpenMode &= ~StreamMode::TRUNC;
752     return bResult;
753 }
754 
755 
756 bool SfxMedium::IsStorage()
757 {
758     if ( pImpl->xStorage.is() )
759         return true;
760 
761     if ( pImpl->m_bTriedStorage )
762         return pImpl->bIsStorage;
763 
764     if ( pImpl->pTempFile )
765     {
766         OUString aURL;
767         if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aURL )
768              != osl::FileBase::E_None )
769         {
770             SAL_WARN( "sfx.doc", "Physical name '" << pImpl->m_aName << "' not convertible to file URL");
771         }
772         pImpl->bIsStorage = SotStorage::IsStorageFile( aURL ) && !SotStorage::IsOLEStorage( aURL);
773         if ( !pImpl->bIsStorage )
774             pImpl->m_bTriedStorage = true;
775     }
776     else if ( GetInStream() )
777     {
778         pImpl->bIsStorage = SotStorage::IsStorageFile( pImpl->m_pInStream.get() ) && !SotStorage::IsOLEStorage( pImpl->m_pInStream.get() );
779         if ( !pImpl->m_pInStream->GetError() && !pImpl->bIsStorage )
780             pImpl->m_bTriedStorage = true;
781     }
782 
783     return pImpl->bIsStorage;
784 }
785 
786 
787 bool SfxMedium::IsPreview_Impl() const
788 {
789     bool bPreview = false;
790     const SfxBoolItem* pPreview = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_PREVIEW, false);
791     if ( pPreview )
792         bPreview = pPreview->GetValue();
793     else
794     {
795         const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_OPTIONS, false);
796         if ( pFlags )
797         {
798             OUString aFileFlags = pFlags->GetValue();
799             aFileFlags = aFileFlags.toAsciiUpperCase();
800             if ( -1 != aFileFlags.indexOf( 'B' ) )
801                 bPreview = true;
802         }
803     }
804 
805     return bPreview;
806 }
807 
808 
809 void SfxMedium::StorageBackup_Impl()
810 {
811     ::ucbhelper::Content aOriginalContent;
812     Reference< css::ucb::XCommandEnvironment > xDummyEnv;
813 
814     bool bBasedOnOriginalFile = ( !pImpl->pTempFile && !( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
815         && !GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ).isEmpty()
816         && GetURLObject().GetProtocol() == INetProtocol::File
817         && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
818 
819     if ( bBasedOnOriginalFile && pImpl->m_aBackupURL.isEmpty()
820       && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aOriginalContent ) )
821     {
822         DoInternalBackup_Impl( aOriginalContent );
823         if( pImpl->m_aBackupURL.isEmpty() )
824             SetError(ERRCODE_SFX_CANTCREATEBACKUP);
825     }
826 }
827 
828 
829 OUString const & SfxMedium::GetBackup_Impl()
830 {
831     if ( pImpl->m_aBackupURL.isEmpty() )
832         StorageBackup_Impl();
833 
834     return pImpl->m_aBackupURL;
835 }
836 
837 
838 uno::Reference < embed::XStorage > SfxMedium::GetOutputStorage()
839 {
840     if ( GetError() )
841         return uno::Reference< embed::XStorage >();
842 
843     // if the medium was constructed with a Storage: use this one, not a temp. storage
844     // if a temporary storage already exists: use it
845     if ( pImpl->xStorage.is() && ( pImpl->m_aLogicName.isEmpty() || pImpl->pTempFile ) )
846         return pImpl->xStorage;
847 
848     // if necessary close stream that was used for reading
849     if ( pImpl->m_pInStream && !pImpl->m_pInStream->IsWritable() )
850         CloseInStream();
851 
852     DBG_ASSERT( !pImpl->m_pOutStream, "OutStream in a readonly Medium?!" );
853 
854     // TODO/LATER: The current solution is to store the document temporary and then copy it to the target location;
855     // in future it should be stored directly and then copied to the temporary location, since in this case no
856     // file attributes have to be preserved and system copying mechanics could be used instead of streaming.
857     CreateTempFileNoCopy();
858 
859     return GetStorage();
860 }
861 
862 
863 void SfxMedium::SetEncryptionDataToStorage_Impl()
864 {
865     // in case media-descriptor contains password it should be used on opening
866     if ( !pImpl->xStorage.is() || !pImpl->m_pSet )
867         return;
868 
869     uno::Sequence< beans::NamedValue > aEncryptionData;
870     if ( !GetEncryptionData_Impl( pImpl->m_pSet.get(), aEncryptionData ) )
871         return;
872 
873     // replace the password with encryption data
874     pImpl->m_pSet->ClearItem( SID_PASSWORD );
875     pImpl->m_pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aEncryptionData ) ) );
876 
877     try
878     {
879         ::comphelper::OStorageHelper::SetCommonStorageEncryptionData( pImpl->xStorage, aEncryptionData );
880     }
881     catch( const uno::Exception& )
882     {
883         SAL_WARN( "sfx.doc", "It must be possible to set a common password for the storage" );
884         // TODO/LATER: set the error code in case of problem
885         // SetError(ERRCODE_IO_GENERAL);
886     }
887 }
888 
889 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
890 
891 // FIXME: Hmm actually lock files should be used for sftp: documents
892 // even if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT. Only the use of lock
893 // files for *local* documents is unnecessary in that case. But
894 // actually, the checks for sftp: here are just wishful thinking; I
895 // don't this there is any support for actually editing documents
896 // behind sftp: URLs anyway.
897 
898 // Sure, there could perhaps be a 3rd-party extension that brings UCB
899 // the potential to handle files behind sftp:. But there could also be
900 // an extension that handles some arbitrary foobar: scheme *and* it
901 // could be that lock files would be the correct thing to use for
902 // foobar: documents, too. But the hardcoded test below won't know
903 // that. Clearly the knowledge whether lock files should be used or
904 // not for some URL scheme belongs in UCB, not here.
905 
906 namespace
907 {
908 
909 OUString tryMSOwnerFiles(const OUString& sDocURL)
910 {
911     svt::MSODocumentLockFile aMSOLockFile(sDocURL);
912     LockFileEntry aData;
913     try
914     {
915         aData = aMSOLockFile.GetLockData();
916     }
917     catch( const uno::Exception& )
918     {
919         return OUString();
920     }
921 
922     OUString sUserData = aData[LockFileComponent::OOOUSERNAME];
923 
924     if (!sUserData.isEmpty())
925         sUserData += " (MS Office)"; // Mention the used office suite
926 
927     return sUserData;
928 }
929 
930 OUString tryForeignLockfiles(const OUString& sDocURL)
931 {
932     OUString sUserData = tryMSOwnerFiles(sDocURL);
933     // here we can test for empty result, and add other known applications' lockfile testing
934     return sUserData.trim();
935 }
936 }
937 
938 SfxMedium::ShowLockResult SfxMedium::ShowLockedDocumentDialog(const LockFileEntry& aData,
939                                                               bool bIsLoading, bool bOwnLock,
940                                                               bool bHandleSysLocked)
941 {
942     ShowLockResult nResult = ShowLockResult::NoLock;
943 
944     // tdf#92817: Simple check for empty lock file that needs to be deleted, when system locking is enabled
945     if( aData[LockFileComponent::OOOUSERNAME].isEmpty() && aData[LockFileComponent::SYSUSERNAME].isEmpty() && !bHandleSysLocked )
946         bOwnLock=true;
947 
948     // show the interaction regarding the document opening
949     uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
950 
951     if ( xHandler.is() && ( bIsLoading || !bHandleSysLocked || bOwnLock ) )
952     {
953         OUString aDocumentURL
954             = GetURLObject().GetLastName(INetURLObject::DecodeMechanism::WithCharset);
955         OUString aInfo;
956         ::rtl::Reference< ::ucbhelper::InteractionRequest > xInteractionRequestImpl;
957 
958         sal_Int32 nContinuations = 3;
959 
960         if ( bOwnLock )
961         {
962             aInfo = aData[LockFileComponent::EDITTIME];
963 
964             xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
965                 document::OwnLockOnDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo, !bIsLoading ) ) );
966         }
967         else
968         {
969             // Use a fourth continuation in case there's no filesystem lock:
970             // "Ignore lock file and open/replace the document"
971             if (!bHandleSysLocked)
972                 nContinuations = 4;
973 
974             if ( !aData[LockFileComponent::OOOUSERNAME].isEmpty() )
975                 aInfo = aData[LockFileComponent::OOOUSERNAME];
976             else
977                 aInfo = aData[LockFileComponent::SYSUSERNAME];
978 
979             if (aInfo.isEmpty() && !GetURLObject().isAnyKnownWebDAVScheme())
980                 // Try to get name of user who has locked the file using other applications
981                 aInfo = tryForeignLockfiles(
982                     GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE));
983 
984             if ( !aInfo.isEmpty() && !aData[LockFileComponent::EDITTIME].isEmpty() )
985                 aInfo += " ( " + aData[LockFileComponent::EDITTIME] + " )";
986 
987             if (!bIsLoading) // so, !bHandleSysLocked
988             {
989                 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny(
990                     document::LockedOnSavingRequest(OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo)));
991                 // Currently, only the last "Retry" continuation (meaning ignore the lock and try overwriting) can be returned.
992             }
993             else /*logically therefore bIsLoading is set */
994             {
995                 xInteractionRequestImpl = new ::ucbhelper::InteractionRequest( uno::makeAny(
996                     document::LockedDocumentRequest( OUString(), uno::Reference< uno::XInterface >(), aDocumentURL, aInfo ) ) );
997             }
998         }
999 
1000         uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(nContinuations);
1001         aContinuations[0] = new ::ucbhelper::InteractionAbort( xInteractionRequestImpl.get() );
1002         aContinuations[1] = new ::ucbhelper::InteractionApprove( xInteractionRequestImpl.get() );
1003         aContinuations[2] = new ::ucbhelper::InteractionDisapprove( xInteractionRequestImpl.get() );
1004         if (nContinuations > 3)
1005         {
1006             // We use InteractionRetry to reflect that user wants to
1007             // ignore the (stale?) alien lock file and open/overwrite the document
1008             aContinuations[3] = new ::ucbhelper::InteractionRetry(xInteractionRequestImpl.get());
1009         }
1010         xInteractionRequestImpl->setContinuations( aContinuations );
1011 
1012         xHandler->handle( xInteractionRequestImpl.get() );
1013 
1014         ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xInteractionRequestImpl->getSelection();
1015         if ( uno::Reference< task::XInteractionAbort >( xSelected.get(), uno::UNO_QUERY ).is() )
1016         {
1017             SetError(ERRCODE_ABORT);
1018         }
1019         else if ( uno::Reference< task::XInteractionDisapprove >( xSelected.get(), uno::UNO_QUERY ).is() )
1020         {
1021             // own lock on loading, user has selected to ignore the lock
1022             // own lock on saving, user has selected to ignore the lock
1023             // alien lock on loading, user has selected to edit a copy of document
1024             // TODO/LATER: alien lock on saving, user has selected to do SaveAs to different location
1025             if ( !bOwnLock ) // bIsLoading implied from outermost condition
1026             {
1027                 // means that a copy of the document should be opened
1028                 GetItemSet()->Put( SfxBoolItem( SID_TEMPLATE, true ) );
1029             }
1030             else
1031                 nResult = ShowLockResult::Succeeded;
1032         }
1033         else if (uno::Reference< task::XInteractionRetry >(xSelected.get(), uno::UNO_QUERY).is())
1034         {
1035             // User decided to ignore the alien (stale?) lock file without filesystem lock
1036             nResult = ShowLockResult::Succeeded;
1037         }
1038         else // if ( XSelected == aContinuations[1] )
1039         {
1040             // own lock on loading, user has selected to open readonly
1041             // own lock on saving, user has selected to open readonly
1042             // alien lock on loading, user has selected to retry saving
1043             // TODO/LATER: alien lock on saving, user has selected to retry saving
1044 
1045             if ( bIsLoading )
1046                 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1047             else
1048                 nResult = ShowLockResult::Try;
1049         }
1050     }
1051     else
1052     {
1053         if ( bIsLoading )
1054         {
1055             // if no interaction handler is provided the default answer is open readonly
1056             // that usually happens in case the document is loaded per API
1057             // so the document must be opened readonly for backward compatibility
1058             GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1059         }
1060         else
1061             SetError(ERRCODE_IO_ACCESSDENIED);
1062 
1063     }
1064 
1065     return nResult;
1066 }
1067 
1068 
1069 bool SfxMedium::ShowLockFileProblemDialog(MessageDlg nWhichDlg)
1070 {
1071     // system file locking is not active, ask user whether he wants to open the document without any locking
1072     uno::Reference< task::XInteractionHandler > xHandler = GetInteractionHandler();
1073 
1074     if (xHandler.is())
1075     {
1076         ::rtl::Reference< ::ucbhelper::InteractionRequest > xIgnoreRequestImpl;
1077 
1078         switch (nWhichDlg)
1079         {
1080             case MessageDlg::LockFileIgnore:
1081                 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileIgnoreRequest() ));
1082                 break;
1083             case MessageDlg::LockFileCorrupt:
1084                 xIgnoreRequestImpl = new ::ucbhelper::InteractionRequest(uno::makeAny( document::LockFileCorruptRequest() ));
1085                 break;
1086         }
1087 
1088         uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations(2);
1089         aContinuations[0] = new ::ucbhelper::InteractionAbort(xIgnoreRequestImpl.get());
1090         aContinuations[1] = new ::ucbhelper::InteractionApprove(xIgnoreRequestImpl.get());
1091         xIgnoreRequestImpl->setContinuations(aContinuations);
1092 
1093         xHandler->handle(xIgnoreRequestImpl.get());
1094 
1095         ::rtl::Reference< ::ucbhelper::InteractionContinuation > xSelected = xIgnoreRequestImpl->getSelection();
1096         bool bReadOnly = uno::Reference< task::XInteractionApprove >(xSelected.get(), uno::UNO_QUERY).is();
1097 
1098         if (bReadOnly)
1099         {
1100             GetItemSet()->Put(SfxBoolItem(SID_DOC_READONLY, true));
1101         }
1102         else
1103         {
1104             SetError(ERRCODE_ABORT);
1105         }
1106 
1107         return bReadOnly;
1108     }
1109 
1110     return false;
1111 }
1112 
1113 namespace
1114 {
1115     bool isSuitableProtocolForLocking(const OUString & rLogicName)
1116     {
1117         INetURLObject aUrl( rLogicName );
1118         INetProtocol eProt = aUrl.GetProtocol();
1119 #if !HAVE_FEATURE_MACOSX_SANDBOX
1120         if (eProt == INetProtocol::File) {
1121             return true;
1122         }
1123 #endif
1124         return eProt == INetProtocol::Smb || eProt == INetProtocol::Sftp;
1125     }
1126 }
1127 
1128 #endif // HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1129 
1130 // sets SID_DOC_READONLY if the document cannot be opened for editing
1131 // if user cancel the loading the ERROR_ABORT is set
1132 SfxMedium::LockFileResult SfxMedium::LockOrigFileOnDemand(bool bLoading, bool bNoUI,
1133                                                           bool bTryIgnoreLockFile,
1134                                                           LockFileEntry* pLockData)
1135 {
1136 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1137     (void) bLoading;
1138     (void) bNoUI;
1139     (void) bTryIgnoreLockFile;
1140     (void) pLockData;
1141     return LockFileResult::Succeeded;
1142 #else
1143     LockFileResult eResult = LockFileResult::Failed;
1144 
1145     // check if path scheme is http:// or https://
1146     // may be this is better if used always, in Android and iOS as well?
1147     // if this code should be always there, remember to move the relevant code in UnlockFile method as well !
1148 
1149     if ( GetURLObject().isAnyKnownWebDAVScheme() )
1150     {
1151         // do nothing if WebDAV locking is disabled
1152         if (!IsWebDAVLockingUsed())
1153             return LockFileResult::Succeeded;
1154 
1155         try
1156         {
1157             bool bResult = pImpl->m_bLocked;
1158             bool bIsTemplate = false;
1159             // so, this is webdav stuff...
1160             if ( !bResult )
1161             {
1162                 // no read-write access is necessary on loading if the document is explicitly opened as copy
1163                 const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
1164                 bIsTemplate = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
1165             }
1166 
1167             if ( !bIsTemplate && !bResult && !IsReadOnly() )
1168             {
1169                 ShowLockResult bUIStatus = ShowLockResult::NoLock;
1170                 do
1171                 {
1172                     if( !bResult )
1173                     {
1174                         uno::Reference< task::XInteractionHandler > xCHandler = GetInteractionHandler( true );
1175                         Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment(
1176                             xCHandler, Reference< css::ucb::XProgressHandler >() );
1177 
1178                         ucbhelper::Content aContentToLock(
1179                             GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1180                             xComEnv, comphelper::getProcessComponentContext() );
1181 
1182                         try
1183                         {
1184                             aContentToLock.lock();
1185                             bResult = true;
1186                         }
1187                         catch ( ucb::InteractiveLockingLockedException& )
1188                         {
1189                             // received when the resource is already locked
1190                             if (!bNoUI || pLockData)
1191                             {
1192                                 // get the lock owner, using a special ucb.webdav property
1193                                 // the owner property retrieved here is  what the other principal send the server
1194                                 // when activating the lock.
1195                                 // See http://tools.ietf.org/html/rfc4918#section-14.17 for details
1196                                 LockFileEntry aLockData;
1197                                 aLockData[LockFileComponent::OOOUSERNAME] = "Unknown user";
1198                                 // This solution works right when the LO user name and the WebDAV user
1199                                 // name are the same.
1200                                 // A better thing to do would be to obtain the 'real' WebDAV user name,
1201                                 // but that's not possible from a WebDAV UCP provider client.
1202                                 LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
1203                                 // use the current LO user name as the system name
1204                                 aLockData[LockFileComponent::SYSUSERNAME]
1205                                     = aOwnData[LockFileComponent::SYSUSERNAME];
1206 
1207                                 uno::Sequence<css::ucb::Lock> aLocks;
1208                                 // getting the property, send a PROPFIND to the server over the net
1209                                 if (aContentToLock.getPropertyValue("DAV:lockdiscovery") >>= aLocks)
1210                                 {
1211                                     // got at least a lock, show the owner of the first lock returned
1212                                     css::ucb::Lock aLock = aLocks[0];
1213                                     OUString aOwner;
1214                                     if (aLock.Owner >>= aOwner)
1215                                     {
1216                                         // we need to display the WebDAV user name owning the lock, not the local one
1217                                         aLockData[LockFileComponent::OOOUSERNAME] = aOwner;
1218                                     }
1219                                 }
1220 
1221                                 if (!bNoUI)
1222                                 {
1223                                     bUIStatus = ShowLockedDocumentDialog(aLockData, bLoading, false,
1224                                                                          true);
1225                                 }
1226 
1227                                 if (pLockData)
1228                                 {
1229                                     std::copy(aLockData.begin(), aLockData.end(), pLockData->begin());
1230                                 }
1231                             }
1232                         }
1233                         catch( ucb::InteractiveNetworkWriteException& )
1234                         {
1235                             // This catch it's not really needed, here just for the sake of documentation on the behaviour.
1236                             // This is the most likely reason:
1237                             // - the remote site is a WebDAV with special configuration: read/only for read operations
1238                             //   and read/write for write operations, the user is not allowed to lock/write and
1239                             //   she cancelled the credentials request.
1240                             //   this is not actually an error, but the exception is sent directly from ucb, avoiding the automatic
1241                             //   management that takes part in cancelCommandExecution()
1242                             // Unfortunately there is no InteractiveNetwork*Exception available to signal this more correctly
1243                             // since it mostly happens on read/only part of webdav, this can be the most correct
1244                             // exception available
1245                         }
1246                         catch( uno::Exception& )
1247                         {}
1248                     }
1249                 } while( !bResult && bUIStatus == ShowLockResult::Try );
1250             }
1251 
1252             pImpl->m_bLocked = bResult;
1253 
1254             if ( !bResult && GetError() == ERRCODE_NONE )
1255             {
1256                 // the error should be set in case it is storing process
1257                 // or the document has been opened for editing explicitly
1258                 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
1259 
1260                 if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
1261                     SetError(ERRCODE_IO_ACCESSDENIED);
1262                 else
1263                     GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1264             }
1265 
1266             // when the file is locked, get the current file date
1267             if ( bResult && DocNeedsFileDateCheck() )
1268                 GetInitFileDate( true );
1269 
1270             if ( bResult )
1271                 eResult = LockFileResult::Succeeded;
1272         }
1273         catch ( const uno::Exception& )
1274         {
1275             TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
1276         }
1277         return eResult;
1278     }
1279 
1280     if (!IsLockingUsed())
1281         return LockFileResult::Succeeded;
1282     if (GetURLObject().HasError())
1283         return eResult;
1284 
1285     try
1286     {
1287         if ( pImpl->m_bLocked && bLoading
1288              && GetURLObject().GetProtocol() == INetProtocol::File )
1289         {
1290             // if the document is already locked the system locking might be temporarily off after storing
1291             // check whether the system file locking should be taken again
1292             GetLockingStream_Impl();
1293         }
1294 
1295         bool bResult = pImpl->m_bLocked;
1296 
1297         if ( !bResult )
1298         {
1299             // no read-write access is necessary on loading if the document is explicitly opened as copy
1300             const SfxBoolItem* pTemplateItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_TEMPLATE, false);
1301             bResult = ( bLoading && pTemplateItem && pTemplateItem->GetValue() );
1302         }
1303 
1304         if ( !bResult && !IsReadOnly() )
1305         {
1306             bool bContentReadonly = false;
1307             if ( bLoading && GetURLObject().GetProtocol() == INetProtocol::File )
1308             {
1309                 // let the original document be opened to check the possibility to open it for editing
1310                 // and to let the writable stream stay open to hold the lock on the document
1311                 GetLockingStream_Impl();
1312             }
1313 
1314             // "IsReadOnly" property does not allow to detect whether the file is readonly always
1315             // so we try always to open the file for editing
1316             // the file is readonly only in case the read-write stream can not be opened
1317             if ( bLoading && !pImpl->m_xLockingStream.is() )
1318             {
1319                 try
1320                 {
1321                     // MediaDescriptor does this check also, the duplication should be avoided in future
1322                     Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1323                     ::ucbhelper::Content aContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext() );
1324                     aContent.getPropertyValue("IsReadOnly") >>= bContentReadonly;
1325                 }
1326                 catch( const uno::Exception& ) {}
1327             }
1328 
1329             // do further checks only if the file not readonly in fs
1330             if ( !bContentReadonly )
1331             {
1332                 // the special file locking should be used only for suitable URLs
1333                 if ( isSuitableProtocolForLocking( pImpl->m_aLogicName ) )
1334                 {
1335 
1336                     // in case of storing the document should request the output before locking
1337                     if ( bLoading )
1338                     {
1339                         // let the stream be opened to check the system file locking
1340                         GetMedium_Impl();
1341                         if (GetError() != ERRCODE_NONE) {
1342                             return eResult;
1343                         }
1344                     }
1345 
1346                     ShowLockResult bUIStatus = ShowLockResult::NoLock;
1347 
1348                     // check whether system file locking has been used, the default value is false
1349                     bool bUseSystemLock = comphelper::isFileUrl( pImpl->m_aLogicName ) && IsSystemFileLockingUsed();
1350 
1351                     // TODO/LATER: This implementation does not allow to detect the system lock on saving here, actually this is no big problem
1352                     // if system lock is used the writeable stream should be available
1353                     bool bHandleSysLocked = ( bLoading && bUseSystemLock && !pImpl->xStream.is() && !pImpl->m_pOutStream );
1354 
1355                     // The file is attempted to get locked for the duration of lockfile creation on save
1356                     std::unique_ptr<osl::File> pFileLock;
1357                     if (!bLoading && bUseSystemLock && pImpl->pTempFile)
1358                     {
1359                         INetURLObject aDest(GetURLObject());
1360                         OUString aDestURL(aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE));
1361 
1362                         if (comphelper::isFileUrl(aDestURL) || !aDest.removeSegment())
1363                         {
1364                             pFileLock = std::make_unique<osl::File>(aDestURL);
1365                             auto rc = pFileLock->open(osl_File_OpenFlag_Write);
1366                             if (rc == osl::FileBase::E_ACCES)
1367                                 bHandleSysLocked = true;
1368                         }
1369                     }
1370 
1371                     do
1372                     {
1373                         try
1374                         {
1375                             ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
1376 
1377                             std::unique_ptr<svt::MSODocumentLockFile> pMSOLockFile;
1378                             const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
1379                             if (rOpt.IsMSOLockFileCreationIsEnabled() && svt::MSODocumentLockFile::IsMSOSupportedFileFormat(pImpl->m_aLogicName))
1380                             {
1381                                 pMSOLockFile.reset(new svt::MSODocumentLockFile(pImpl->m_aLogicName));
1382                                 pImpl->m_bMSOLockFileCreated = true;
1383                             }
1384 
1385                             bool  bIoErr = false;
1386 
1387                             if (!bHandleSysLocked)
1388                             {
1389                                 try
1390                                 {
1391                                     bResult = aLockFile.CreateOwnLockFile();
1392                                     if(pMSOLockFile)
1393                                         bResult &= pMSOLockFile->CreateOwnLockFile();
1394                                 }
1395                                 catch (const uno::Exception&)
1396                                 {
1397                                     if (tools::IsMappedWebDAVPath(GetURLObject().GetMainURL(
1398                                             INetURLObject::DecodeMechanism::NONE)))
1399                                     {
1400                                         // This is a path that redirects to a WebDAV resource;
1401                                         // so failure creating lockfile is not an error here.
1402                                         bResult = true;
1403                                     }
1404                                     else if (bLoading && !bNoUI)
1405                                     {
1406                                         bIoErr = true;
1407                                         ShowLockFileProblemDialog(MessageDlg::LockFileIgnore);
1408                                         bResult = true;   // always delete the defect lock-file
1409                                     }
1410                                 }
1411 
1412                                 // in case OOo locking is turned off the lock file is still written if possible
1413                                 // but it is ignored while deciding whether the document should be opened for editing or not
1414                                 if (!bResult && !IsOOoLockFileUsed() && !bIoErr)
1415                                 {
1416                                     bResult = true;
1417                                     // take the ownership over the lock file
1418                                     aLockFile.OverwriteOwnLockFile();
1419 
1420                                     if(pMSOLockFile)
1421                                         pMSOLockFile->OverwriteOwnLockFile();
1422                                 }
1423                             }
1424 
1425                             if ( !bResult )
1426                             {
1427                                 LockFileEntry aData;
1428                                 try
1429                                 {
1430                                     aData = aLockFile.GetLockData();
1431                                 }
1432                                 catch (const io::WrongFormatException&)
1433                                 {
1434                                     // we get empty or corrupt data
1435                                     // info to the user
1436                                     if (!bIoErr && bLoading && !bNoUI )
1437                                         bResult = ShowLockFileProblemDialog(MessageDlg::LockFileCorrupt);
1438 
1439                                     // not show the Lock Document Dialog
1440                                     bIoErr = true;
1441                                 }
1442                                 catch( const uno::Exception& )
1443                                 {
1444                                     // show the Lock Document Dialog, when locked from other app
1445                                     bIoErr = !bHandleSysLocked;
1446                                 }
1447 
1448                                 bool bOwnLock = false;
1449 
1450                                 if (!bHandleSysLocked)
1451                                 {
1452                                     LockFileEntry aOwnData = svt::LockFileCommon::GenerateOwnEntry();
1453                                     bOwnLock = aOwnData[LockFileComponent::SYSUSERNAME] == aData[LockFileComponent::SYSUSERNAME];
1454 
1455                                     if (bOwnLock
1456                                         && aOwnData[LockFileComponent::LOCALHOST] == aData[LockFileComponent::LOCALHOST]
1457                                         && aOwnData[LockFileComponent::USERURL] == aData[LockFileComponent::USERURL])
1458                                     {
1459                                         // this is own lock from the same installation, it could remain because of crash
1460                                         bResult = true;
1461                                     }
1462                                 }
1463 
1464                                 if ( !bResult && !bIoErr)
1465                                 {
1466                                     if (!bNoUI)
1467                                         bUIStatus = ShowLockedDocumentDialog(
1468                                             aData, bLoading, bOwnLock, bHandleSysLocked);
1469                                     else if (bLoading && bTryIgnoreLockFile && !bHandleSysLocked)
1470                                         bUIStatus = ShowLockResult::Succeeded;
1471 
1472                                     if ( bUIStatus == ShowLockResult::Succeeded )
1473                                     {
1474                                         // take the ownership over the lock file
1475                                         bResult = aLockFile.OverwriteOwnLockFile();
1476 
1477                                         if(pMSOLockFile)
1478                                             pMSOLockFile->OverwriteOwnLockFile();
1479                                     }
1480                                     else if (bLoading && !bHandleSysLocked)
1481                                         eResult = LockFileResult::FailedLockFile;
1482 
1483                                     if (!bResult && pLockData)
1484                                     {
1485                                         std::copy(aData.begin(), aData.end(), pLockData->begin());
1486                                     }
1487                                 }
1488                             }
1489                         }
1490                         catch( const uno::Exception& )
1491                         {
1492                         }
1493                     } while( !bResult && bUIStatus == ShowLockResult::Try );
1494 
1495                     pImpl->m_bLocked = bResult;
1496                 }
1497                 else
1498                 {
1499                     // this is no file URL, check whether the file is readonly
1500                     bResult = !bContentReadonly;
1501                 }
1502             }
1503         }
1504 
1505         if ( !bResult && GetError() == ERRCODE_NONE )
1506         {
1507             // the error should be set in case it is storing process
1508             // or the document has been opened for editing explicitly
1509             const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
1510 
1511             if ( !bLoading || (pReadOnlyItem && !pReadOnlyItem->GetValue()) )
1512                 SetError(ERRCODE_IO_ACCESSDENIED);
1513             else
1514                 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1515         }
1516 
1517         // when the file is locked, get the current file date
1518         if ( bResult && DocNeedsFileDateCheck() )
1519             GetInitFileDate( true );
1520 
1521         if ( bResult )
1522             eResult = LockFileResult::Succeeded;
1523     }
1524     catch( const uno::Exception& )
1525     {
1526         TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: high probability, that the content has not been created" );
1527     }
1528 
1529     return eResult;
1530 #endif
1531 }
1532 
1533 
1534 uno::Reference < embed::XStorage > SfxMedium::GetStorage( bool bCreateTempFile )
1535 {
1536     if ( pImpl->xStorage.is() || pImpl->m_bTriedStorage )
1537         return pImpl->xStorage;
1538 
1539     uno::Sequence< uno::Any > aArgs( 2 );
1540 
1541     // the medium should be retrieved before temporary file creation
1542     // to let the MediaDescriptor be filled with the streams
1543     GetMedium_Impl();
1544 
1545     if ( bCreateTempFile )
1546         CreateTempFile( false );
1547 
1548     GetMedium_Impl();
1549 
1550     if ( GetError() )
1551         return pImpl->xStorage;
1552 
1553     const SfxBoolItem* pRepairItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_REPAIRPACKAGE, false);
1554     if ( pRepairItem && pRepairItem->GetValue() )
1555     {
1556         // the storage should be created for repairing mode
1557         CreateTempFile( false );
1558         GetMedium_Impl();
1559 
1560         Reference< css::ucb::XProgressHandler > xProgressHandler;
1561         Reference< css::task::XStatusIndicator > xStatusIndicator;
1562 
1563         const SfxUnoAnyItem* pxProgressItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetItemSet(), SID_PROGRESS_STATUSBAR_CONTROL, false);
1564         if( pxProgressItem && ( pxProgressItem->GetValue() >>= xStatusIndicator ) )
1565             xProgressHandler.set( new utl::ProgressHandlerWrap( xStatusIndicator ) );
1566 
1567         uno::Sequence< beans::PropertyValue > aAddProps( 2 );
1568         aAddProps[0].Name = "RepairPackage";
1569         aAddProps[0].Value <<= true;
1570         aAddProps[1].Name = "StatusIndicator";
1571         aAddProps[1].Value <<= xProgressHandler;
1572 
1573         // the first arguments will be filled later
1574         aArgs.realloc( 3 );
1575         aArgs[2] <<= aAddProps;
1576     }
1577 
1578     if ( pImpl->xStream.is() )
1579     {
1580         // since the storage is based on temporary stream we open it always read-write
1581         aArgs[0] <<= pImpl->xStream;
1582         aArgs[1] <<= embed::ElementModes::READWRITE;
1583         pImpl->bStorageBasedOnInStream = true;
1584         if (pImpl->m_bDisableFileSync)
1585         {
1586             // Forward NoFileSync to the storage factory.
1587             aArgs.realloc(3);
1588             uno::Sequence<beans::PropertyValue> aProperties(
1589                 comphelper::InitPropertySequence({ { "NoFileSync", uno::makeAny(true) } }));
1590             aArgs[2] <<= aProperties;
1591         }
1592     }
1593     else if ( pImpl->xInputStream.is() )
1594     {
1595         // since the storage is based on temporary stream we open it always read-write
1596         aArgs[0] <<= pImpl->xInputStream;
1597         aArgs[1] <<= embed::ElementModes::READ;
1598         pImpl->bStorageBasedOnInStream = true;
1599     }
1600     else
1601     {
1602         CloseStreams_Impl();
1603         aArgs[0] <<= pImpl->m_aName;
1604         aArgs[1] <<= embed::ElementModes::READ;
1605         pImpl->bStorageBasedOnInStream = false;
1606     }
1607 
1608     try
1609     {
1610         pImpl->xStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ),
1611                             uno::UNO_QUERY );
1612     }
1613     catch( const uno::Exception& )
1614     {
1615         // impossibility to create the storage is no error
1616     }
1617 
1618     if( ( pImpl->nLastStorageError = GetError() ) != ERRCODE_NONE )
1619     {
1620         pImpl->xStorage = nullptr;
1621         if ( pImpl->m_pInStream )
1622             pImpl->m_pInStream->Seek(0);
1623         return uno::Reference< embed::XStorage >();
1624     }
1625 
1626     pImpl->m_bTriedStorage = true;
1627 
1628     // TODO/LATER: Get versionlist on demand
1629     if ( pImpl->xStorage.is() )
1630     {
1631         SetEncryptionDataToStorage_Impl();
1632         GetVersionList();
1633     }
1634 
1635     const SfxInt16Item* pVersion = SfxItemSet::GetItem<SfxInt16Item>(pImpl->m_pSet.get(), SID_VERSION, false);
1636 
1637     bool bResetStorage = false;
1638     if ( pVersion && pVersion->GetValue() )
1639     {
1640         // Read all available versions
1641         if ( pImpl->aVersions.hasElements() )
1642         {
1643             // Search for the version fits the comment
1644             // The versions are numbered starting with 1, versions with
1645             // negative versions numbers are counted backwards from the
1646             // current version
1647             short nVersion = pVersion->GetValue();
1648             if ( nVersion<0 )
1649                 nVersion = static_cast<short>(pImpl->aVersions.getLength()) + nVersion;
1650             else // nVersion > 0; pVersion->GetValue() != 0 was the condition to this block
1651                 nVersion--;
1652 
1653             util::RevisionTag& rTag = pImpl->aVersions[nVersion];
1654             {
1655                 // Open SubStorage for all versions
1656                 uno::Reference < embed::XStorage > xSub = pImpl->xStorage->openStorageElement( "Versions",
1657                         embed::ElementModes::READ );
1658 
1659                 DBG_ASSERT( xSub.is(), "Version list, but no Versions!" );
1660 
1661                 // There the version is stored as packed Stream
1662                 uno::Reference < io::XStream > xStr = xSub->openStreamElement( rTag.Identifier, embed::ElementModes::READ );
1663                 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream( xStr ));
1664                 if ( pStream && pStream->GetError() == ERRCODE_NONE )
1665                 {
1666                     // Unpack Stream  in TempDir
1667                     ::utl::TempFile aTempFile;
1668                     const OUString& aTmpName = aTempFile.GetURL();
1669                     SvFileStream    aTmpStream( aTmpName, SFX_STREAM_READWRITE );
1670 
1671                     pStream->ReadStream( aTmpStream );
1672                     pStream.reset();
1673                     aTmpStream.Close();
1674 
1675                     // Open data as Storage
1676                     pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
1677                     pImpl->xStorage = comphelper::OStorageHelper::GetStorageFromURL( aTmpName, embed::ElementModes::READ );
1678                     pImpl->bStorageBasedOnInStream = false;
1679                     OUString aTemp;
1680                     osl::FileBase::getSystemPathFromFileURL( aTmpName, aTemp );
1681                     SetPhysicalName_Impl( aTemp );
1682 
1683                     pImpl->bIsTemp = true;
1684                     GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
1685                     // TODO/MBA
1686                     pImpl->aVersions.realloc(0);
1687                 }
1688                 else
1689                     bResetStorage = true;
1690             }
1691         }
1692         else
1693             bResetStorage = true;
1694     }
1695 
1696     if ( bResetStorage )
1697     {
1698         pImpl->xStorage.clear();
1699         if ( pImpl->m_pInStream )
1700             pImpl->m_pInStream->Seek( 0 );
1701     }
1702 
1703     pImpl->bIsStorage = pImpl->xStorage.is();
1704     return pImpl->xStorage;
1705 }
1706 
1707 
1708 uno::Reference< embed::XStorage > const & SfxMedium::GetZipStorageToSign_Impl( bool bReadOnly )
1709 {
1710     if ( !GetError() && !pImpl->m_xZipStorage.is() )
1711     {
1712         GetMedium_Impl();
1713 
1714         try
1715         {
1716             // we can not sign document if there is no stream
1717             // should it be possible at all?
1718             if ( !bReadOnly && pImpl->xStream.is() )
1719             {
1720                 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
1721             }
1722             else if ( pImpl->xInputStream.is() )
1723             {
1724                 pImpl->m_xZipStorage = ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xInputStream );
1725             }
1726         }
1727         catch( const uno::Exception& )
1728         {
1729             SAL_WARN( "sfx.doc", "No possibility to get readonly version of storage from medium!" );
1730         }
1731 
1732         if ( GetError() ) // do not remove warnings
1733             ResetError();
1734     }
1735 
1736     return pImpl->m_xZipStorage;
1737 }
1738 
1739 
1740 void SfxMedium::CloseZipStorage_Impl()
1741 {
1742     if ( pImpl->m_xZipStorage.is() )
1743     {
1744         try {
1745             pImpl->m_xZipStorage->dispose();
1746         } catch( const uno::Exception& )
1747         {}
1748 
1749         pImpl->m_xZipStorage.clear();
1750     }
1751 }
1752 
1753 void SfxMedium::CloseStorage()
1754 {
1755     if ( pImpl->xStorage.is() )
1756     {
1757         uno::Reference < lang::XComponent > xComp = pImpl->xStorage;
1758         // in the salvage mode the medium does not own the storage
1759         if ( pImpl->bDisposeStorage && !pImpl->m_bSalvageMode )
1760         {
1761             try {
1762                 xComp->dispose();
1763             } catch( const uno::Exception& )
1764             {
1765                 SAL_WARN( "sfx.doc", "Medium's storage is already disposed!" );
1766             }
1767         }
1768 
1769         pImpl->xStorage.clear();
1770         pImpl->bStorageBasedOnInStream = false;
1771     }
1772 
1773     pImpl->m_bTriedStorage = false;
1774     pImpl->bIsStorage = false;
1775 }
1776 
1777 void SfxMedium::CanDisposeStorage_Impl( bool bDisposeStorage )
1778 {
1779     pImpl->bDisposeStorage = bDisposeStorage;
1780 }
1781 
1782 bool SfxMedium::WillDisposeStorageOnClose_Impl()
1783 {
1784     return pImpl->bDisposeStorage;
1785 }
1786 
1787 StreamMode SfxMedium::GetOpenMode() const
1788 {
1789     return pImpl->m_nStorOpenMode;
1790 }
1791 
1792 void SfxMedium::SetOpenMode( StreamMode nStorOpen,
1793                              bool bDontClose )
1794 {
1795     if ( pImpl->m_nStorOpenMode != nStorOpen )
1796     {
1797         pImpl->m_nStorOpenMode = nStorOpen;
1798 
1799         if( !bDontClose )
1800         {
1801             if ( pImpl->xStorage.is() )
1802                 CloseStorage();
1803 
1804             CloseStreams_Impl();
1805         }
1806     }
1807 }
1808 
1809 
1810 bool SfxMedium::UseBackupToRestore_Impl( ::ucbhelper::Content& aOriginalContent,
1811                                             const Reference< css::ucb::XCommandEnvironment >& xComEnv )
1812 {
1813     try
1814     {
1815         ::ucbhelper::Content aTransactCont( pImpl->m_aBackupURL, xComEnv, comphelper::getProcessComponentContext() );
1816 
1817         Reference< XInputStream > aOrigInput = aTransactCont.openStream();
1818         aOriginalContent.writeStream( aOrigInput, true );
1819         return true;
1820     }
1821     catch( const Exception& )
1822     {
1823         // in case of failure here the backup file should not be removed
1824         // TODO/LATER: a message should be used to let user know about the backup
1825         pImpl->m_bRemoveBackup = false;
1826         // TODO/LATER: needs a specific error code
1827         pImpl->m_eError = ERRCODE_IO_GENERAL;
1828     }
1829 
1830     return false;
1831 }
1832 
1833 
1834 bool SfxMedium::StorageCommit_Impl()
1835 {
1836     bool bResult = false;
1837     Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1838     ::ucbhelper::Content aOriginalContent;
1839 
1840     if ( pImpl->xStorage.is() )
1841     {
1842         if ( !GetError() )
1843         {
1844             uno::Reference < embed::XTransactedObject > xTrans( pImpl->xStorage, uno::UNO_QUERY );
1845             if ( xTrans.is() )
1846             {
1847                 try
1848                 {
1849                     xTrans->commit();
1850                     CloseZipStorage_Impl();
1851                     bResult = true;
1852                 }
1853                 catch ( const embed::UseBackupException& aBackupExc )
1854                 {
1855                     // since the temporary file is created always now, the scenario is close to be impossible
1856                     if ( !pImpl->pTempFile )
1857                     {
1858                         OSL_ENSURE( !pImpl->m_aBackupURL.isEmpty(), "No backup on storage commit!" );
1859                         if ( !pImpl->m_aBackupURL.isEmpty()
1860                             && ::ucbhelper::Content::create( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
1861                                                         xDummyEnv, comphelper::getProcessComponentContext(),
1862                                                         aOriginalContent ) )
1863                         {
1864                             // use backup to restore the file
1865                             // the storage has already disconnected from original location
1866                             CloseAndReleaseStreams_Impl();
1867                             if ( !UseBackupToRestore_Impl( aOriginalContent, xDummyEnv ) )
1868                             {
1869                                 // connect the medium to the temporary file of the storage
1870                                 pImpl->aContent = ::ucbhelper::Content();
1871                                 pImpl->m_aName = aBackupExc.TemporaryFileURL;
1872                                 OSL_ENSURE( !pImpl->m_aName.isEmpty(), "The exception _must_ contain the temporary URL!" );
1873                             }
1874                         }
1875                     }
1876 
1877                     if (!GetError())
1878                         SetError(ERRCODE_IO_GENERAL);
1879                 }
1880                 catch ( const uno::Exception& )
1881                 {
1882                     //TODO/LATER: improve error handling
1883                     SetError(ERRCODE_IO_GENERAL);
1884                 }
1885             }
1886         }
1887     }
1888 
1889     return bResult;
1890 }
1891 
1892 
1893 void SfxMedium::TransactedTransferForFS_Impl( const INetURLObject& aSource,
1894                                                  const INetURLObject& aDest,
1895                                                  const Reference< css::ucb::XCommandEnvironment >& xComEnv )
1896 {
1897     bool bResult = false;
1898     Reference< css::ucb::XCommandEnvironment > xDummyEnv;
1899     ::ucbhelper::Content aOriginalContent;
1900 
1901     try
1902     {
1903         aOriginalContent = ::ucbhelper::Content( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
1904     }
1905     catch ( const css::ucb::CommandAbortedException& )
1906     {
1907         pImpl->m_eError = ERRCODE_ABORT;
1908     }
1909     catch ( const css::ucb::CommandFailedException& )
1910     {
1911         pImpl->m_eError = ERRCODE_ABORT;
1912     }
1913     catch (const css::ucb::ContentCreationException& ex)
1914     {
1915         pImpl->m_eError = ERRCODE_IO_GENERAL;
1916         if (
1917             (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER    ) ||
1918             (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
1919            )
1920         {
1921             pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
1922         }
1923     }
1924     catch (const css::uno::Exception&)
1925     {
1926        pImpl->m_eError = ERRCODE_IO_GENERAL;
1927     }
1928 
1929     if( pImpl->m_eError && !pImpl->m_eError.IsWarning() )
1930         return;
1931 
1932     if ( pImpl->xStorage.is() )
1933         CloseStorage();
1934 
1935     CloseStreams_Impl();
1936 
1937     ::ucbhelper::Content aTempCont;
1938     if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xDummyEnv, comphelper::getProcessComponentContext(), aTempCont ) )
1939     {
1940         bool bTransactStarted = false;
1941         const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
1942         bool bOverWrite = !pOverWrite || pOverWrite->GetValue();
1943 
1944         try
1945         {
1946             OUString aSourceMainURL = aSource.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1947             OUString aDestMainURL = aDest.GetMainURL(INetURLObject::DecodeMechanism::NONE);
1948 
1949             sal_uInt64 nAttributes = GetDefaultFileAttributes(aDestMainURL);
1950             if (IsFileMovable(aDest)
1951                 && osl::File::replace(aSourceMainURL, aDestMainURL) == osl::FileBase::E_None)
1952             {
1953                 if (nAttributes)
1954                     // Adjust attributes, source might be created with
1955                     // the osl_File_OpenFlag_Private flag.
1956                     osl::File::setAttributes(aDestMainURL, nAttributes);
1957                 bResult = true;
1958             }
1959             else
1960             {
1961                 if (bOverWrite && ::utl::UCBContentHelper::IsDocument(aDestMainURL))
1962                 {
1963                     if( pImpl->m_aBackupURL.isEmpty() )
1964                         DoInternalBackup_Impl( aOriginalContent );
1965 
1966                     if( !pImpl->m_aBackupURL.isEmpty() )
1967                     {
1968                         Reference< XInputStream > aTempInput = aTempCont.openStream();
1969                         bTransactStarted = true;
1970                         aOriginalContent.setPropertyValue( "Size", uno::makeAny( sal_Int64(0) ) );
1971                         aOriginalContent.writeStream( aTempInput, bOverWrite );
1972                         bResult = true;
1973                     }
1974                     else
1975                     {
1976                         pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
1977                     }
1978                 }
1979                 else
1980                 {
1981                     Reference< XInputStream > aTempInput = aTempCont.openStream();
1982                     aOriginalContent.writeStream( aTempInput, bOverWrite );
1983                     bResult = true;
1984                 }
1985             }
1986         }
1987         catch ( const css::ucb::CommandAbortedException& )
1988         {
1989             pImpl->m_eError = ERRCODE_ABORT;
1990         }
1991         catch ( const css::ucb::CommandFailedException& )
1992         {
1993             pImpl->m_eError = ERRCODE_ABORT;
1994         }
1995         catch ( const css::ucb::InteractiveIOException& r )
1996         {
1997             if ( r.Code == IOErrorCode_ACCESS_DENIED )
1998                 pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
1999             else if ( r.Code == IOErrorCode_NOT_EXISTING )
2000                 pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2001             else if ( r.Code == IOErrorCode_CANT_READ )
2002                 pImpl->m_eError = ERRCODE_IO_CANTREAD;
2003             else
2004                 pImpl->m_eError = ERRCODE_IO_GENERAL;
2005         }
2006         catch ( const css::uno::Exception& )
2007         {
2008             pImpl->m_eError = ERRCODE_IO_GENERAL;
2009         }
2010 
2011         if ( bResult )
2012         {
2013             if ( pImpl->pTempFile )
2014             {
2015                 pImpl->pTempFile->EnableKillingFile();
2016                 pImpl->pTempFile.reset();
2017             }
2018         }
2019         else if ( bTransactStarted )
2020         {
2021             UseBackupToRestore_Impl( aOriginalContent, xDummyEnv );
2022         }
2023     }
2024     else
2025         pImpl->m_eError = ERRCODE_IO_CANTREAD;
2026 }
2027 
2028 
2029 bool SfxMedium::TryDirectTransfer( const OUString& aURL, SfxItemSet const & aTargetSet )
2030 {
2031     if ( GetError() )
2032         return false;
2033 
2034     // if the document had no password it should be stored without password
2035     // if the document had password it should be stored with the same password
2036     // otherwise the stream copying can not be done
2037     const SfxStringItem* pNewPassItem = aTargetSet.GetItem<SfxStringItem>(SID_PASSWORD, false);
2038     const SfxStringItem* pOldPassItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_PASSWORD, false);
2039     if ( ( !pNewPassItem && !pOldPassItem )
2040       || ( pNewPassItem && pOldPassItem && pNewPassItem->GetValue() == pOldPassItem->GetValue() ) )
2041     {
2042         // the filter must be the same
2043         const SfxStringItem* pNewFilterItem = aTargetSet.GetItem<SfxStringItem>(SID_FILTER_NAME, false);
2044         const SfxStringItem* pOldFilterItem = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_FILTER_NAME, false);
2045         if ( pNewFilterItem && pOldFilterItem && pNewFilterItem->GetValue() == pOldFilterItem->GetValue() )
2046         {
2047             // get the input stream and copy it
2048             // in case of success return true
2049             uno::Reference< io::XInputStream > xInStream = GetInputStream();
2050 
2051             ResetError();
2052             if ( xInStream.is() )
2053             {
2054                 try
2055                 {
2056                     uno::Reference< io::XSeekable > xSeek( xInStream, uno::UNO_QUERY );
2057                     sal_Int64 nPos = 0;
2058                     if ( xSeek.is() )
2059                     {
2060                         nPos = xSeek->getPosition();
2061                         xSeek->seek( 0 );
2062                     }
2063 
2064                     uno::Reference < css::ucb::XCommandEnvironment > xEnv;
2065                     ::ucbhelper::Content aTargetContent( aURL, xEnv, comphelper::getProcessComponentContext() );
2066 
2067                     InsertCommandArgument aInsertArg;
2068                     aInsertArg.Data = xInStream;
2069                     const SfxBoolItem* pOverWrite = aTargetSet.GetItem<SfxBoolItem>(SID_OVERWRITE, false);
2070                     if ( pOverWrite && !pOverWrite->GetValue() ) // argument says: never overwrite
2071                         aInsertArg.ReplaceExisting = false;
2072                     else
2073                         aInsertArg.ReplaceExisting = true; // default is overwrite existing files
2074 
2075                     Any aCmdArg;
2076                     aCmdArg <<= aInsertArg;
2077                     aTargetContent.executeCommand( "insert",
2078                                                     aCmdArg );
2079 
2080                     if ( xSeek.is() )
2081                         xSeek->seek( nPos );
2082 
2083                     return true;
2084                 }
2085                 catch( const uno::Exception& )
2086                 {}
2087             }
2088         }
2089     }
2090 
2091     return false;
2092 }
2093 
2094 
2095 void SfxMedium::Transfer_Impl()
2096 {
2097     // The transfer is required only in two cases: either if there is a temporary file or if there is a salvage item
2098     OUString aNameURL;
2099     if ( pImpl->pTempFile )
2100         aNameURL = pImpl->pTempFile->GetURL();
2101     else if ( !pImpl->m_aLogicName.isEmpty() && pImpl->m_bSalvageMode )
2102     {
2103         // makes sense only in case logic name is set
2104         if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aNameURL )
2105              != osl::FileBase::E_None )
2106             SAL_WARN( "sfx.doc", "The medium name is not convertible!" );
2107     }
2108 
2109     if ( aNameURL.isEmpty() || ( pImpl->m_eError && !pImpl->m_eError.IsWarning() ) )
2110         return;
2111 
2112     SAL_INFO( "sfx.doc", "SfxMedium::Transfer_Impl, copying to target" );
2113 
2114     Reference < css::ucb::XCommandEnvironment > xEnv;
2115     Reference< XOutputStream > rOutStream;
2116 
2117     // in case an output stream is provided from outside and the URL is correct
2118     // commit to the stream
2119     if (pImpl->m_aLogicName.startsWith("private:stream"))
2120     {
2121         // TODO/LATER: support storing to SID_STREAM
2122         const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
2123         if( pOutStreamItem && ( pOutStreamItem->GetValue() >>= rOutStream ) )
2124         {
2125             if ( pImpl->xStorage.is() )
2126                 CloseStorage();
2127 
2128             CloseStreams_Impl();
2129 
2130             INetURLObject aSource( aNameURL );
2131             ::ucbhelper::Content aTempCont;
2132             if( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aTempCont ) )
2133             {
2134                 try
2135                 {
2136                     sal_Int32 nRead;
2137                     sal_Int32 nBufferSize = 32767;
2138                     Sequence < sal_Int8 > aSequence ( nBufferSize );
2139                     Reference< XInputStream > aTempInput = aTempCont.openStream();
2140 
2141                     do
2142                     {
2143                         nRead = aTempInput->readBytes ( aSequence, nBufferSize );
2144                         if ( nRead < nBufferSize )
2145                         {
2146                             Sequence < sal_Int8 > aTempBuf ( aSequence.getConstArray(), nRead );
2147                             rOutStream->writeBytes ( aTempBuf );
2148                         }
2149                         else
2150                             rOutStream->writeBytes ( aSequence );
2151                     }
2152                     while ( nRead == nBufferSize );
2153 
2154                     // remove temporary file
2155                     if ( pImpl->pTempFile )
2156                     {
2157                         pImpl->pTempFile->EnableKillingFile();
2158                         pImpl->pTempFile.reset();
2159                     }
2160                 }
2161                 catch( const Exception& )
2162                 {}
2163             }
2164         }
2165         else
2166         {
2167             SAL_WARN( "sfx.doc", "Illegal Output stream parameter!" );
2168             SetError(ERRCODE_IO_GENERAL);
2169         }
2170 
2171         // free the reference
2172         if ( pImpl->m_pSet )
2173             pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
2174 
2175         return;
2176     }
2177 
2178     GetContent();
2179     if ( !pImpl->aContent.get().is() )
2180     {
2181         pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2182         return;
2183     }
2184 
2185     INetURLObject aDest( GetURLObject() );
2186 
2187     // source is the temp file written so far
2188     INetURLObject aSource( aNameURL );
2189 
2190     // a special case, an interaction handler should be used for
2191     // authentication in case it is available
2192     Reference< css::ucb::XCommandEnvironment > xComEnv;
2193     Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
2194     if (xInteractionHandler.is())
2195         xComEnv = new ::ucbhelper::CommandEnvironment( xInteractionHandler,
2196                                                   Reference< css::ucb::XProgressHandler >() );
2197 
2198     OUString aDestURL( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2199 
2200     if ( comphelper::isFileUrl( aDestURL ) || !aDest.removeSegment() )
2201     {
2202         TransactedTransferForFS_Impl( aSource, aDest, xComEnv );
2203 
2204         if (!pImpl->m_bDisableFileSync)
2205         {
2206             // Hideous - no clean way to do this, so we re-open the file just to fsync it
2207             osl::File aFile( aDestURL );
2208             if ( aFile.open( osl_File_OpenFlag_Write ) == osl::FileBase::E_None )
2209             {
2210                 aFile.sync();
2211                 SAL_INFO( "sfx.doc", "fsync'd saved file '" << aDestURL << "'" );
2212                 aFile.close();
2213             }
2214         }
2215     }
2216     else
2217     {
2218         // create content for the parent folder and call transfer on that content with the source content
2219         // and the destination file name as parameters
2220         ::ucbhelper::Content aSourceContent;
2221         ::ucbhelper::Content aTransferContent;
2222 
2223         ::ucbhelper::Content aDestContent;
2224         (void)::ucbhelper::Content::create( aDestURL, xComEnv, comphelper::getProcessComponentContext(), aDestContent );
2225         // For checkin, we need the object URL, not the parent folder:
2226         if ( !IsInCheckIn( ) )
2227         {
2228             // Get the parent URL from the XChild if possible: why would the URL necessarily have
2229             // a hierarchical path? It's not always the case for CMIS.
2230             Reference< css::container::XChild> xChild( aDestContent.get(), uno::UNO_QUERY );
2231             OUString sParentUrl;
2232             if ( xChild.is( ) )
2233             {
2234                 Reference< css::ucb::XContent > xParent( xChild->getParent( ), uno::UNO_QUERY );
2235                 if ( xParent.is( ) )
2236                 {
2237                     sParentUrl = xParent->getIdentifier( )->getContentIdentifier();
2238                 }
2239             }
2240 
2241             if ( sParentUrl.isEmpty() )
2242                 aDestURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2243                     // adjust to above aDest.removeSegment()
2244             else
2245                 aDestURL = sParentUrl;
2246         }
2247 
2248         // LongName wasn't defined anywhere, only used here... get the Title instead
2249         // as it's less probably empty
2250         OUString aFileName;
2251         Any aAny = aDestContent.getPropertyValue("Title");
2252         aAny >>= aFileName;
2253         aAny = aDestContent.getPropertyValue( "ObjectId" );
2254         OUString sObjectId;
2255         aAny >>= sObjectId;
2256         if ( aFileName.isEmpty() )
2257             aFileName = GetURLObject().getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2258 
2259         try
2260         {
2261             aTransferContent = ::ucbhelper::Content( aDestURL, xComEnv, comphelper::getProcessComponentContext() );
2262         }
2263         catch (const css::ucb::ContentCreationException& ex)
2264         {
2265             pImpl->m_eError = ERRCODE_IO_GENERAL;
2266             if (
2267                 (ex.eError == css::ucb::ContentCreationError_NO_CONTENT_PROVIDER    ) ||
2268                 (ex.eError == css::ucb::ContentCreationError_CONTENT_CREATION_FAILED)
2269                )
2270             {
2271                 pImpl->m_eError = ERRCODE_IO_NOTEXISTSPATH;
2272             }
2273         }
2274         catch (const css::uno::Exception&)
2275         {
2276             pImpl->m_eError = ERRCODE_IO_GENERAL;
2277         }
2278 
2279         if ( !pImpl->m_eError || pImpl->m_eError.IsWarning() )
2280         {
2281             // free resources, otherwise the transfer may fail
2282             if ( pImpl->xStorage.is() )
2283                 CloseStorage();
2284 
2285             CloseStreams_Impl();
2286 
2287             (void)::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent );
2288 
2289             // check for external parameters that may customize the handling of NameClash situations
2290             const SfxBoolItem* pOverWrite = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_OVERWRITE, false);
2291             sal_Int32 nNameClash;
2292             if ( pOverWrite && !pOverWrite->GetValue() )
2293                 // argument says: never overwrite
2294                 nNameClash = NameClash::ERROR;
2295             else
2296                 // default is overwrite existing files
2297                 nNameClash = NameClash::OVERWRITE;
2298 
2299             try
2300             {
2301                 OUString aMimeType = pImpl->getFilterMimeType();
2302                 ::ucbhelper::InsertOperation eOperation = ::ucbhelper::InsertOperation::Copy;
2303                 bool bMajor = false;
2304                 OUString sComment;
2305                 if ( IsInCheckIn( ) )
2306                 {
2307                     eOperation = ::ucbhelper::InsertOperation::Checkin;
2308                     const SfxBoolItem* pMajor = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOCINFO_MAJOR, false);
2309                     bMajor = pMajor && pMajor->GetValue( );
2310                     const SfxStringItem* pComments = SfxItemSet::GetItem<SfxStringItem>(GetItemSet(), SID_DOCINFO_COMMENTS, false);
2311                     if ( pComments )
2312                         sComment = pComments->GetValue( );
2313                 }
2314                 OUString sResultURL;
2315                 aTransferContent.transferContent(
2316                     aSourceContent, eOperation,
2317                     aFileName, nNameClash, aMimeType, bMajor, sComment,
2318                     &sResultURL, sObjectId );
2319 
2320                 if ( !sResultURL.isEmpty( ) )  // Likely to happen only for checkin
2321                     SwitchDocumentToFile( sResultURL );
2322                 try
2323                 {
2324                     if ( GetURLObject().isAnyKnownWebDAVScheme() &&
2325                          eOperation == ::ucbhelper::InsertOperation::Copy )
2326                     {
2327                         // tdf#95272 try to re-issue a lock command when a new file is created.
2328                         // This may be needed because some WebDAV servers fail to implement the
2329                         // 'LOCK on unallocated reference', see issue comment:
2330                         // <https://bugs.documentfoundation.org/show_bug.cgi?id=95792#c8>
2331                         // and specification at:
2332                         // <http://tools.ietf.org/html/rfc4918#section-7.3>
2333                         // If the WebDAV resource is already locked by this LO instance, nothing will
2334                         // happen, e.g. the LOCK method will not be sent to the server.
2335                         ::ucbhelper::Content aLockContent( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
2336                         aLockContent.lock();
2337                     }
2338                 }
2339                 catch ( css::uno::Exception & )
2340                 {
2341                     TOOLS_WARN_EXCEPTION( "sfx.doc", "LOCK not working while re-issuing it" );
2342                 }
2343             }
2344             catch ( const css::ucb::CommandAbortedException& )
2345             {
2346                 pImpl->m_eError = ERRCODE_ABORT;
2347             }
2348             catch ( const css::ucb::CommandFailedException& )
2349             {
2350                 pImpl->m_eError = ERRCODE_ABORT;
2351             }
2352             catch ( const css::ucb::InteractiveIOException& r )
2353             {
2354                 if ( r.Code == IOErrorCode_ACCESS_DENIED )
2355                     pImpl->m_eError = ERRCODE_IO_ACCESSDENIED;
2356                 else if ( r.Code == IOErrorCode_NOT_EXISTING )
2357                     pImpl->m_eError = ERRCODE_IO_NOTEXISTS;
2358                 else if ( r.Code == IOErrorCode_CANT_READ )
2359                     pImpl->m_eError = ERRCODE_IO_CANTREAD;
2360                 else
2361                     pImpl->m_eError = ERRCODE_IO_GENERAL;
2362             }
2363             catch ( const css::uno::Exception& )
2364             {
2365                 pImpl->m_eError = ERRCODE_IO_GENERAL;
2366             }
2367 
2368             // do not switch from temporary file in case of nonfile protocol
2369         }
2370     }
2371 
2372     if ( ( !pImpl->m_eError || pImpl->m_eError.IsWarning() ) && !pImpl->pTempFile )
2373     {
2374         // without a TempFile the physical and logical name should be the same after successful transfer
2375         if (osl::FileBase::getSystemPathFromFileURL(
2376               GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName )
2377             != osl::FileBase::E_None)
2378         {
2379             pImpl->m_aName.clear();
2380         }
2381         pImpl->m_bSalvageMode = false;
2382     }
2383 }
2384 
2385 
2386 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent,
2387                                        const OUString& aPrefix,
2388                                        const OUString& aExtension,
2389                                        const OUString& aDestDir )
2390 {
2391     if ( !pImpl->m_aBackupURL.isEmpty() )
2392         return; // the backup was done already
2393 
2394     ::utl::TempFile aTransactTemp( aPrefix, true, &aExtension, &aDestDir );
2395 
2396     INetURLObject aBackObj( aTransactTemp.GetURL() );
2397     OUString aBackupName = aBackObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2398 
2399     Reference < css::ucb::XCommandEnvironment > xDummyEnv;
2400     ::ucbhelper::Content aBackupCont;
2401     if( ::ucbhelper::Content::create( aDestDir, xDummyEnv, comphelper::getProcessComponentContext(), aBackupCont ) )
2402     {
2403         try
2404         {
2405             OUString sMimeType = pImpl->getFilterMimeType();
2406             aBackupCont.transferContent( aOriginalContent,
2407                                             ::ucbhelper::InsertOperation::Copy,
2408                                             aBackupName,
2409                                             NameClash::OVERWRITE,
2410                                             sMimeType );
2411             pImpl->m_aBackupURL = aBackObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2412             pImpl->m_bRemoveBackup = true;
2413         }
2414         catch( const Exception& )
2415         {}
2416     }
2417 
2418     if ( pImpl->m_aBackupURL.isEmpty() )
2419         aTransactTemp.EnableKillingFile();
2420 }
2421 
2422 
2423 void SfxMedium::DoInternalBackup_Impl( const ::ucbhelper::Content& aOriginalContent )
2424 {
2425     if ( !pImpl->m_aBackupURL.isEmpty() )
2426         return; // the backup was done already
2427 
2428     OUString aFileName =  GetURLObject().getName( INetURLObject::LAST_SEGMENT,
2429                                                         true,
2430                                                         INetURLObject::DecodeMechanism::NONE );
2431 
2432     sal_Int32 nPrefixLen = aFileName.lastIndexOf( '.' );
2433     OUString aPrefix = ( nPrefixLen == -1 ) ? aFileName : aFileName.copy( 0, nPrefixLen );
2434     OUString aExtension = ( nPrefixLen == -1 ) ? OUString() : aFileName.copy( nPrefixLen );
2435     OUString aBakDir = SvtPathOptions().GetBackupPath();
2436 
2437     // create content for the parent folder ( = backup folder )
2438     ::ucbhelper::Content  aContent;
2439     Reference < css::ucb::XCommandEnvironment > xEnv;
2440     if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
2441         DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aBakDir );
2442 
2443     if ( !pImpl->m_aBackupURL.isEmpty() )
2444         return;
2445 
2446     // the copying to the backup catalog failed ( for example because
2447     // of using an encrypted partition as target catalog )
2448     // since the user did not specify to make backup explicitly
2449     // office should try to make backup in another place,
2450     // target catalog does not look bad for this case ( and looks
2451     // to be the only way for encrypted partitions )
2452 
2453     INetURLObject aDest = GetURLObject();
2454     if ( aDest.removeSegment() )
2455         DoInternalBackup_Impl( aOriginalContent, aPrefix, aExtension, aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
2456 }
2457 
2458 
2459 void SfxMedium::DoBackup_Impl()
2460 {
2461     // source file name is the logical name of this medium
2462     INetURLObject aSource( GetURLObject() );
2463 
2464     // there is nothing to backup in case source file does not exist
2465     if ( !::utl::UCBContentHelper::IsDocument( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
2466         return;
2467 
2468     bool        bSuccess = false;
2469 
2470     // get path for backups
2471     OUString aBakDir = SvtPathOptions().GetBackupPath();
2472     if( !aBakDir.isEmpty() )
2473     {
2474         // create content for the parent folder ( = backup folder )
2475         ::ucbhelper::Content  aContent;
2476         Reference < css::ucb::XCommandEnvironment > xEnv;
2477         if( ::utl::UCBContentHelper::ensureFolder(comphelper::getProcessComponentContext(), xEnv, aBakDir, aContent) )
2478         {
2479             // save as ".bak" file
2480             INetURLObject aDest( aBakDir );
2481             aDest.insertName( aSource.getName() );
2482             aDest.setExtension( "bak" );
2483             OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
2484 
2485             // create a content for the source file
2486             ::ucbhelper::Content aSourceContent;
2487             if ( ::ucbhelper::Content::create( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext(), aSourceContent ) )
2488             {
2489                 try
2490                 {
2491                     // do the transfer ( copy source file to backup dir )
2492                     OUString sMimeType = pImpl->getFilterMimeType();
2493                     aContent.transferContent( aSourceContent,
2494                                                         ::ucbhelper::InsertOperation::Copy,
2495                                                         aFileName,
2496                                                         NameClash::OVERWRITE,
2497                                                         sMimeType );
2498                     pImpl->m_aBackupURL = aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE );
2499                     pImpl->m_bRemoveBackup = false;
2500                     bSuccess = true;
2501                 }
2502                 catch ( const css::uno::Exception& )
2503                 {
2504                 }
2505             }
2506         }
2507     }
2508 
2509     if ( !bSuccess )
2510     {
2511         pImpl->m_eError = ERRCODE_SFX_CANTCREATEBACKUP;
2512     }
2513 }
2514 
2515 
2516 void SfxMedium::ClearBackup_Impl()
2517 {
2518     if( pImpl->m_bRemoveBackup )
2519     {
2520         // currently a document is always stored in a new medium,
2521         // thus if a backup can not be removed the backup URL should not be cleaned
2522         if ( !pImpl->m_aBackupURL.isEmpty() )
2523         {
2524             if ( ::utl::UCBContentHelper::Kill( pImpl->m_aBackupURL ) )
2525             {
2526                 pImpl->m_bRemoveBackup = false;
2527                 pImpl->m_aBackupURL.clear();
2528             }
2529             else
2530             {
2531 
2532                 SAL_WARN( "sfx.doc", "Couldn't remove backup file!");
2533             }
2534         }
2535     }
2536     else
2537         pImpl->m_aBackupURL.clear();
2538 }
2539 
2540 
2541 void SfxMedium::GetLockingStream_Impl()
2542 {
2543     if ( GetURLObject().GetProtocol() != INetProtocol::File
2544          || pImpl->m_xLockingStream.is() )
2545         return;
2546 
2547     const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
2548     if ( pWriteStreamItem )
2549         pWriteStreamItem->GetValue() >>= pImpl->m_xLockingStream;
2550 
2551     if ( pImpl->m_xLockingStream.is() )
2552         return;
2553 
2554     // open the original document
2555     uno::Sequence< beans::PropertyValue > xProps;
2556     TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
2557     utl::MediaDescriptor aMedium( xProps );
2558 
2559     aMedium.addInputStreamOwnLock();
2560 
2561     uno::Reference< io::XInputStream > xInputStream;
2562     aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->m_xLockingStream;
2563     aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= xInputStream;
2564 
2565     if ( !pImpl->pTempFile && pImpl->m_aName.isEmpty() )
2566     {
2567         // the medium is still based on the original file, it makes sense to initialize the streams
2568         if ( pImpl->m_xLockingStream.is() )
2569             pImpl->xStream = pImpl->m_xLockingStream;
2570 
2571         if ( xInputStream.is() )
2572             pImpl->xInputStream = xInputStream;
2573 
2574         if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2575             pImpl->xInputStream = pImpl->xStream->getInputStream();
2576     }
2577 }
2578 
2579 
2580 void SfxMedium::GetMedium_Impl()
2581 {
2582     if ( pImpl->m_pInStream
2583         && (!pImpl->bIsTemp || pImpl->xInputStream.is() || pImpl->m_xInputStreamToLoadFrom.is() || pImpl->xStream.is() || pImpl->m_xLockingStream.is() ) )
2584         return;
2585 
2586     pImpl->bDownloadDone = false;
2587     Reference< css::task::XInteractionHandler > xInteractionHandler = GetInteractionHandler();
2588 
2589     //TODO/MBA: need support for SID_STREAM
2590     const SfxUnoAnyItem* pWriteStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_STREAM, false);
2591     const SfxUnoAnyItem* pInStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INPUTSTREAM, false);
2592     if ( pWriteStreamItem )
2593     {
2594         pWriteStreamItem->GetValue() >>= pImpl->xStream;
2595 
2596         if ( pInStreamItem )
2597             pInStreamItem->GetValue() >>= pImpl->xInputStream;
2598 
2599         if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2600             pImpl->xInputStream = pImpl->xStream->getInputStream();
2601     }
2602     else if ( pInStreamItem )
2603     {
2604         pInStreamItem->GetValue() >>= pImpl->xInputStream;
2605     }
2606     else
2607     {
2608         uno::Sequence < beans::PropertyValue > xProps;
2609         OUString aFileName;
2610         if (!pImpl->m_aName.isEmpty())
2611         {
2612             if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aFileName )
2613                  != osl::FileBase::E_None )
2614             {
2615                 SAL_WARN( "sfx.doc", "Physical name not convertible!");
2616             }
2617         }
2618         else
2619             aFileName = GetName();
2620 
2621         // in case the temporary file exists the streams should be initialized from it,
2622         // but the original MediaDescriptor should not be changed
2623         bool bFromTempFile = ( pImpl->pTempFile != nullptr );
2624 
2625         if ( !bFromTempFile )
2626         {
2627             GetItemSet()->Put( SfxStringItem( SID_FILE_NAME, aFileName ) );
2628             if( !(pImpl->m_nStorOpenMode & StreamMode::WRITE) )
2629                 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
2630             if (xInteractionHandler.is())
2631                 GetItemSet()->Put( SfxUnoAnyItem( SID_INTERACTIONHANDLER, makeAny(xInteractionHandler) ) );
2632         }
2633 
2634         if ( pImpl->m_xInputStreamToLoadFrom.is() )
2635         {
2636             pImpl->xInputStream = pImpl->m_xInputStreamToLoadFrom;
2637             if (pImpl->m_bInputStreamIsReadOnly)
2638                 GetItemSet()->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
2639         }
2640         else
2641         {
2642             TransformItems( SID_OPENDOC, *GetItemSet(), xProps );
2643             utl::MediaDescriptor aMedium( xProps );
2644 
2645             if ( pImpl->m_xLockingStream.is() && !bFromTempFile )
2646             {
2647                 // the medium is not based on the temporary file, so the original stream can be used
2648                 pImpl->xStream = pImpl->m_xLockingStream;
2649             }
2650             else
2651             {
2652                 if ( bFromTempFile )
2653                 {
2654                     aMedium[utl::MediaDescriptor::PROP_URL()] <<= aFileName;
2655                     aMedium.erase( utl::MediaDescriptor::PROP_READONLY() );
2656                     aMedium.addInputStream();
2657                 }
2658                 else if ( GetURLObject().GetProtocol() == INetProtocol::File )
2659                 {
2660                     // use the special locking approach only for file URLs
2661                     aMedium.addInputStreamOwnLock();
2662                 }
2663                 else
2664                 {
2665                     // add a check for protocol, if it's http or https or provide webdav then add
2666                     // the interaction handler to be used by the authentication dialog
2667                     if ( GetURLObject().isAnyKnownWebDAVScheme() )
2668                     {
2669                         aMedium[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER()] <<= GetInteractionHandler( true );
2670                     }
2671                     aMedium.addInputStream();
2672                 }
2673                 // the ReadOnly property set in aMedium is ignored
2674                 // the check is done in LockOrigFileOnDemand() for file and non-file URLs
2675 
2676                 //TODO/MBA: what happens if property is not there?!
2677                 aMedium[utl::MediaDescriptor::PROP_STREAM()] >>= pImpl->xStream;
2678                 aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM()] >>= pImpl->xInputStream;
2679             }
2680 
2681             GetContent();
2682             if ( !pImpl->xInputStream.is() && pImpl->xStream.is() )
2683                 pImpl->xInputStream = pImpl->xStream->getInputStream();
2684         }
2685 
2686         if ( !bFromTempFile )
2687         {
2688             //TODO/MBA: need support for SID_STREAM
2689             if ( pImpl->xStream.is() )
2690                 GetItemSet()->Put( SfxUnoAnyItem( SID_STREAM, makeAny( pImpl->xStream ) ) );
2691 
2692             GetItemSet()->Put( SfxUnoAnyItem( SID_INPUTSTREAM, makeAny( pImpl->xInputStream ) ) );
2693         }
2694     }
2695 
2696     //TODO/MBA: ErrorHandling - how to transport error from MediaDescriptor
2697     if ( !GetError() && !pImpl->xStream.is() && !pImpl->xInputStream.is() )
2698         SetError(ERRCODE_IO_ACCESSDENIED);
2699 
2700     if ( !GetError() && !pImpl->m_pInStream )
2701     {
2702         if ( pImpl->xStream.is() )
2703             pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xStream );
2704         else if ( pImpl->xInputStream.is() )
2705             pImpl->m_pInStream = utl::UcbStreamHelper::CreateStream( pImpl->xInputStream );
2706     }
2707 
2708     pImpl->bDownloadDone = true;
2709     pImpl->aDoneLink.ClearPendingCall();
2710     ErrCode nError = GetError();
2711     pImpl->aDoneLink.Call( reinterpret_cast<void*>(sal_uInt32(nError)) );
2712 }
2713 
2714 bool SfxMedium::IsRemote() const
2715 {
2716     return pImpl->m_bRemote;
2717 }
2718 
2719 void SfxMedium::SetUpdatePickList(bool bVal)
2720 {
2721     pImpl->bUpdatePickList = bVal;
2722 }
2723 
2724 bool SfxMedium::IsUpdatePickList() const
2725 {
2726     return pImpl->bUpdatePickList;
2727 }
2728 
2729 void SfxMedium::SetLongName(const OUString &rName)
2730 {
2731     pImpl->m_aLongName = rName;
2732 }
2733 
2734 const OUString& SfxMedium::GetLongName() const
2735 {
2736     return pImpl->m_aLongName;
2737 }
2738 
2739 void SfxMedium::SetDoneLink( const Link<void*,void>& rLink )
2740 {
2741     pImpl->aDoneLink = rLink;
2742 }
2743 
2744 void SfxMedium::Download( const Link<void*,void>& aLink )
2745 {
2746     SetDoneLink( aLink );
2747     GetInStream();
2748     if ( pImpl->m_pInStream && !aLink.IsSet() )
2749     {
2750         while( !pImpl->bDownloadDone )
2751             Application::Yield();
2752     }
2753 }
2754 
2755 
2756 void SfxMedium::Init_Impl()
2757 /*  [Description]
2758     Includes a valid:: sun:: com:: star:: util:: URL (If a file name was
2759     previously in there) in the logical name and if available sets the
2760     physical name as the file name.
2761  */
2762 
2763 {
2764     Reference< XOutputStream > rOutStream;
2765 
2766     // TODO/LATER: handle lifetime of storages
2767     pImpl->bDisposeStorage = false;
2768 
2769     const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
2770     if ( pSalvageItem && pSalvageItem->GetValue().isEmpty() )
2771     {
2772         pSalvageItem = nullptr;
2773         pImpl->m_pSet->ClearItem( SID_DOC_SALVAGE );
2774     }
2775 
2776     if (!pImpl->m_aLogicName.isEmpty())
2777     {
2778         INetURLObject aUrl( pImpl->m_aLogicName );
2779         INetProtocol eProt = aUrl.GetProtocol();
2780         if ( eProt == INetProtocol::NotValid )
2781         {
2782             SAL_WARN( "sfx.doc", "URL <" << pImpl->m_aLogicName << "> with unknown protocol" );
2783         }
2784         else
2785         {
2786             if ( aUrl.HasMark() )
2787             {
2788                 pImpl->m_aLogicName = aUrl.GetURLNoMark( INetURLObject::DecodeMechanism::NONE );
2789                 GetItemSet()->Put( SfxStringItem( SID_JUMPMARK, aUrl.GetMark() ) );
2790             }
2791 
2792             // try to convert the URL into a physical name - but never change a physical name
2793             // physical name may be set if the logical name is changed after construction
2794             if ( pImpl->m_aName.isEmpty() )
2795                 osl::FileBase::getSystemPathFromFileURL( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), pImpl->m_aName );
2796             else
2797             {
2798                 DBG_ASSERT( pSalvageItem, "Suspicious change of logical name!" );
2799             }
2800         }
2801     }
2802 
2803     if ( pSalvageItem && !pSalvageItem->GetValue().isEmpty() )
2804     {
2805         pImpl->m_aLogicName = pSalvageItem->GetValue();
2806         pImpl->m_pURLObj.reset();
2807         pImpl->m_bSalvageMode = true;
2808     }
2809 
2810     // in case output stream is by mistake here
2811     // clear the reference
2812     const SfxUnoAnyItem* pOutStreamItem = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_OUTPUTSTREAM, false);
2813     if( pOutStreamItem
2814      && ( !( pOutStreamItem->GetValue() >>= rOutStream )
2815           || !pImpl->m_aLogicName.startsWith("private:stream")) )
2816     {
2817         pImpl->m_pSet->ClearItem( SID_OUTPUTSTREAM );
2818         SAL_WARN( "sfx.doc", "Unexpected Output stream parameter!" );
2819     }
2820 
2821     if (!pImpl->m_aLogicName.isEmpty())
2822     {
2823         // if the logic name is set it should be set in MediaDescriptor as well
2824         const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
2825         if ( !pFileNameItem )
2826         {
2827             // let the ItemSet be created if necessary
2828             GetItemSet()->Put(
2829                 SfxStringItem(
2830                     SID_FILE_NAME, INetURLObject( pImpl->m_aLogicName ).GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) );
2831         }
2832     }
2833 
2834     SetIsRemote_Impl();
2835 
2836     osl::DirectoryItem item;
2837     if (osl::DirectoryItem::get(GetName(), item) == osl::FileBase::E_None) {
2838         osl::FileStatus stat(osl_FileStatus_Mask_Attributes);
2839         if (item.getFileStatus(stat) == osl::FileBase::E_None
2840             && stat.isValid(osl_FileStatus_Mask_Attributes))
2841         {
2842             if ((stat.getAttributes() & osl_File_Attribute_ReadOnly) != 0) {
2843                 pImpl->m_bOriginallyReadOnly = true;
2844             }
2845         }
2846     }
2847 }
2848 
2849 
2850 SfxMedium::SfxMedium() : pImpl(new SfxMedium_Impl)
2851 {
2852     Init_Impl();
2853 }
2854 
2855 
2856 void SfxMedium::UseInteractionHandler( bool bUse )
2857 {
2858     pImpl->bAllowDefaultIntHdl = bUse;
2859 }
2860 
2861 
2862 css::uno::Reference< css::task::XInteractionHandler >
2863 SfxMedium::GetInteractionHandler( bool bGetAlways )
2864 {
2865     // if interaction isn't allowed explicitly ... return empty reference!
2866     if ( !bGetAlways && !pImpl->bUseInteractionHandler )
2867         return css::uno::Reference< css::task::XInteractionHandler >();
2868 
2869     // search a possible existing handler inside cached item set
2870     if ( pImpl->m_pSet )
2871     {
2872         css::uno::Reference< css::task::XInteractionHandler > xHandler;
2873         const SfxUnoAnyItem* pHandler = SfxItemSet::GetItem<SfxUnoAnyItem>(pImpl->m_pSet.get(), SID_INTERACTIONHANDLER, false);
2874         if ( pHandler && (pHandler->GetValue() >>= xHandler) && xHandler.is() )
2875             return xHandler;
2876     }
2877 
2878     // if default interaction isn't allowed explicitly ... return empty reference!
2879     if ( !bGetAlways && !pImpl->bAllowDefaultIntHdl )
2880         return css::uno::Reference< css::task::XInteractionHandler >();
2881 
2882     // otherwise return cached default handler ... if it exist.
2883     if ( pImpl->xInteraction.is() )
2884         return pImpl->xInteraction;
2885 
2886     // create default handler and cache it!
2887     Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2888     pImpl->xInteraction.set(
2889         task::InteractionHandler::createWithParent(xContext, nullptr), UNO_QUERY_THROW );
2890     return pImpl->xInteraction;
2891 }
2892 
2893 
2894 void SfxMedium::SetFilter( const std::shared_ptr<const SfxFilter>& pFilter )
2895 {
2896     pImpl->m_pFilter = pFilter;
2897 }
2898 
2899 const std::shared_ptr<const SfxFilter>& SfxMedium::GetFilter() const
2900 {
2901     return pImpl->m_pFilter;
2902 }
2903 
2904 sal_uInt32 SfxMedium::CreatePasswordToModifyHash( const OUString& aPasswd, bool bWriter )
2905 {
2906     sal_uInt32 nHash = 0;
2907 
2908     if ( !aPasswd.isEmpty() )
2909     {
2910         if ( bWriter )
2911         {
2912             nHash = ::comphelper::DocPasswordHelper::GetWordHashAsUINT32( aPasswd );
2913         }
2914         else
2915         {
2916             rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
2917             nHash = ::comphelper::DocPasswordHelper::GetXLHashAsUINT16( aPasswd, nEncoding );
2918         }
2919     }
2920 
2921     return nHash;
2922 }
2923 
2924 
2925 void SfxMedium::Close(bool bInDestruction)
2926 {
2927     if ( pImpl->xStorage.is() )
2928     {
2929         CloseStorage();
2930     }
2931 
2932     CloseStreams_Impl(bInDestruction);
2933 
2934     UnlockFile( false );
2935 }
2936 
2937 void SfxMedium::CloseAndRelease()
2938 {
2939     if ( pImpl->xStorage.is() )
2940     {
2941         CloseStorage();
2942     }
2943 
2944     CloseAndReleaseStreams_Impl();
2945 
2946     UnlockFile( true );
2947 }
2948 
2949 void SfxMedium::DisableUnlockWebDAV( bool bDisableUnlockWebDAV )
2950 {
2951     pImpl->m_bDisableUnlockWebDAV = bDisableUnlockWebDAV;
2952 }
2953 
2954 void SfxMedium::DisableFileSync(bool bDisableFileSync)
2955 {
2956     pImpl->m_bDisableFileSync = bDisableFileSync;
2957 }
2958 
2959 void SfxMedium::UnlockFile( bool bReleaseLockStream )
2960 {
2961 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
2962     (void) bReleaseLockStream;
2963 #else
2964     // check if webdav
2965     if ( GetURLObject().isAnyKnownWebDAVScheme() )
2966     {
2967         // do nothing if WebDAV locking if disabled
2968         // (shouldn't happen because we already skipped locking,
2969         // see LockOrigFileOnDemand, but just in case ...)
2970         if (!IsWebDAVLockingUsed())
2971             return;
2972 
2973         if ( pImpl->m_bLocked )
2974         {
2975             // an interaction handler should be used for authentication, if needed
2976             try {
2977                 uno::Reference< css::task::XInteractionHandler > xHandler = GetInteractionHandler( true );
2978                 uno::Reference< css::ucb::XCommandEnvironment > xComEnv = new ::ucbhelper::CommandEnvironment( xHandler,
2979                                                                Reference< css::ucb::XProgressHandler >() );
2980                 ucbhelper::Content aContentToUnlock( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext());
2981                 pImpl->m_bLocked = false;
2982                 //check if WebDAV unlock was explicitly disabled
2983                 if ( !pImpl->m_bDisableUnlockWebDAV )
2984                     aContentToUnlock.unlock();
2985             }
2986             catch ( uno::Exception& )
2987             {
2988                 TOOLS_WARN_EXCEPTION( "sfx.doc", "Locking exception: WebDAV while trying to lock the file" );
2989             }
2990         }
2991         return;
2992     }
2993 
2994     if ( pImpl->m_xLockingStream.is() )
2995     {
2996         if ( bReleaseLockStream )
2997         {
2998             try
2999             {
3000                 uno::Reference< io::XInputStream > xInStream = pImpl->m_xLockingStream->getInputStream();
3001                 uno::Reference< io::XOutputStream > xOutStream = pImpl->m_xLockingStream->getOutputStream();
3002                 if ( xInStream.is() )
3003                     xInStream->closeInput();
3004                 if ( xOutStream.is() )
3005                     xOutStream->closeOutput();
3006             }
3007             catch( const uno::Exception& )
3008             {}
3009         }
3010 
3011         pImpl->m_xLockingStream.clear();
3012     }
3013 
3014     if ( !pImpl->m_bLocked )
3015         return;
3016 
3017     ::svt::DocumentLockFile aLockFile( pImpl->m_aLogicName );
3018 
3019     try
3020     {
3021         pImpl->m_bLocked = false;
3022         // TODO/LATER: A warning could be shown in case the file is not the own one
3023         aLockFile.RemoveFile();
3024     }
3025     catch( const io::WrongFormatException& )
3026     {
3027         try
3028         {
3029             // erase the empty or corrupt file
3030             aLockFile.RemoveFileDirectly();
3031         }
3032         catch( const uno::Exception& )
3033         {}
3034     }
3035     catch( const uno::Exception& )
3036     {}
3037 
3038     if(pImpl->m_bMSOLockFileCreated)
3039     {
3040         ::svt::MSODocumentLockFile aMSOLockFile( pImpl->m_aLogicName );
3041 
3042         try
3043         {
3044             pImpl->m_bLocked = false;
3045             // TODO/LATER: A warning could be shown in case the file is not the own one
3046             aMSOLockFile.RemoveFile();
3047         }
3048         catch( const io::WrongFormatException& )
3049         {
3050             try
3051             {
3052                 // erase the empty or corrupt file
3053                 aMSOLockFile.RemoveFileDirectly();
3054             }
3055             catch( const uno::Exception& )
3056             {}
3057         }
3058         catch( const uno::Exception& )
3059         {}
3060         pImpl->m_bMSOLockFileCreated = false;
3061     }
3062 #endif
3063 }
3064 
3065 void SfxMedium::CloseAndReleaseStreams_Impl()
3066 {
3067     CloseZipStorage_Impl();
3068 
3069     uno::Reference< io::XInputStream > xInToClose = pImpl->xInputStream;
3070     uno::Reference< io::XOutputStream > xOutToClose;
3071     if ( pImpl->xStream.is() )
3072     {
3073         xOutToClose = pImpl->xStream->getOutputStream();
3074 
3075         // if the locking stream is closed here the related member should be cleaned
3076         if ( pImpl->xStream == pImpl->m_xLockingStream )
3077             pImpl->m_xLockingStream.clear();
3078     }
3079 
3080     // The probably existing SvStream wrappers should be closed first
3081     CloseStreams_Impl();
3082 
3083     // in case of salvage mode the storage is based on the streams
3084     if ( pImpl->m_bSalvageMode )
3085         return;
3086 
3087     try
3088     {
3089         if ( xInToClose.is() )
3090             xInToClose->closeInput();
3091         if ( xOutToClose.is() )
3092             xOutToClose->closeOutput();
3093     }
3094     catch ( const uno::Exception& )
3095     {
3096     }
3097 }
3098 
3099 
3100 void SfxMedium::CloseStreams_Impl(bool bInDestruction)
3101 {
3102     CloseInStream_Impl(bInDestruction);
3103     CloseOutStream_Impl();
3104 
3105     if ( pImpl->m_pSet )
3106         pImpl->m_pSet->ClearItem( SID_CONTENT );
3107 
3108     pImpl->aContent = ::ucbhelper::Content();
3109 }
3110 
3111 
3112 void SfxMedium::SetIsRemote_Impl()
3113 {
3114     INetURLObject aObj( GetName() );
3115     switch( aObj.GetProtocol() )
3116     {
3117         case INetProtocol::Ftp:
3118         case INetProtocol::Http:
3119         case INetProtocol::Https:
3120             pImpl->m_bRemote = true;
3121         break;
3122         default:
3123             pImpl->m_bRemote = GetName().startsWith("private:msgid");
3124             break;
3125     }
3126 
3127     // As files that are written to the remote transmission must also be able
3128     // to be read.
3129     if (pImpl->m_bRemote)
3130         pImpl->m_nStorOpenMode |= StreamMode::READ;
3131 }
3132 
3133 
3134 void SfxMedium::SetName( const OUString& aNameP, bool bSetOrigURL )
3135 {
3136     if (pImpl->aOrigURL.isEmpty())
3137         pImpl->aOrigURL = pImpl->m_aLogicName;
3138     if( bSetOrigURL )
3139         pImpl->aOrigURL = aNameP;
3140     pImpl->m_aLogicName = aNameP;
3141     pImpl->m_pURLObj.reset();
3142     pImpl->aContent = ::ucbhelper::Content();
3143     Init_Impl();
3144 }
3145 
3146 
3147 const OUString& SfxMedium::GetOrigURL() const
3148 {
3149     return pImpl->aOrigURL.isEmpty() ? pImpl->m_aLogicName : pImpl->aOrigURL;
3150 }
3151 
3152 
3153 void SfxMedium::SetPhysicalName_Impl( const OUString& rNameP )
3154 {
3155     if ( rNameP != pImpl->m_aName )
3156     {
3157         pImpl->pTempFile.reset();
3158 
3159         if ( !pImpl->m_aName.isEmpty() || !rNameP.isEmpty() )
3160             pImpl->aContent = ::ucbhelper::Content();
3161 
3162         pImpl->m_aName = rNameP;
3163         pImpl->m_bTriedStorage = false;
3164         pImpl->bIsStorage = false;
3165     }
3166 }
3167 
3168 
3169 void SfxMedium::ReOpen()
3170 {
3171     bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
3172     pImpl->bUseInteractionHandler = false;
3173     GetMedium_Impl();
3174     pImpl->bUseInteractionHandler = bUseInteractionHandler;
3175 }
3176 
3177 
3178 void SfxMedium::CompleteReOpen()
3179 {
3180     // do not use temporary file for reopen and in case of success throw the temporary file away
3181     bool bUseInteractionHandler = pImpl->bUseInteractionHandler;
3182     pImpl->bUseInteractionHandler = false;
3183 
3184     std::unique_ptr<::utl::TempFile> pTmpFile;
3185     if ( pImpl->pTempFile )
3186     {
3187         pTmpFile = std::move(pImpl->pTempFile);
3188         pImpl->m_aName.clear();
3189     }
3190 
3191     GetMedium_Impl();
3192 
3193     if ( GetError() )
3194     {
3195         if ( pImpl->pTempFile )
3196         {
3197             pImpl->pTempFile->EnableKillingFile();
3198             pImpl->pTempFile.reset();
3199         }
3200         pImpl->pTempFile = std::move( pTmpFile );
3201         if ( pImpl->pTempFile )
3202             pImpl->m_aName = pImpl->pTempFile->GetFileName();
3203     }
3204     else if (pTmpFile)
3205     {
3206         pTmpFile->EnableKillingFile();
3207         pTmpFile.reset();
3208     }
3209 
3210     pImpl->bUseInteractionHandler = bUseInteractionHandler;
3211 }
3212 
3213 SfxMedium::SfxMedium(const OUString &rName, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, std::unique_ptr<SfxItemSet> pInSet) :
3214     pImpl(new SfxMedium_Impl)
3215 {
3216     pImpl->m_pSet = std::move( pInSet );
3217     pImpl->m_pFilter = std::move(pFilter);
3218     pImpl->m_aLogicName = rName;
3219     pImpl->m_nStorOpenMode = nOpenMode;
3220     Init_Impl();
3221 }
3222 
3223 SfxMedium::SfxMedium(const OUString &rName, const OUString &rReferer, StreamMode nOpenMode, std::shared_ptr<const SfxFilter> pFilter, std::unique_ptr<SfxItemSet> pInSet) :
3224     pImpl(new SfxMedium_Impl)
3225 {
3226     pImpl->m_pSet = std::move(pInSet);
3227     SfxItemSet * s = GetItemSet();
3228     if (s->GetItem(SID_REFERER) == nullptr) {
3229         s->Put(SfxStringItem(SID_REFERER, rReferer));
3230     }
3231     pImpl->m_pFilter = std::move(pFilter);
3232     pImpl->m_aLogicName = rName;
3233     pImpl->m_nStorOpenMode = nOpenMode;
3234     Init_Impl();
3235 }
3236 
3237 SfxMedium::SfxMedium( const uno::Sequence<beans::PropertyValue>& aArgs ) :
3238     pImpl(new SfxMedium_Impl)
3239 {
3240     SfxAllItemSet *pParams = new SfxAllItemSet( SfxGetpApp()->GetPool() );
3241     pImpl->m_pSet.reset( pParams );
3242     TransformParameters( SID_OPENDOC, aArgs, *pParams );
3243 
3244     OUString aFilterProvider, aFilterName;
3245     {
3246         const SfxPoolItem* pItem = nullptr;
3247         if (pImpl->m_pSet->HasItem(SID_FILTER_PROVIDER, &pItem))
3248             aFilterProvider = static_cast<const SfxStringItem*>(pItem)->GetValue();
3249 
3250         if (pImpl->m_pSet->HasItem(SID_FILTER_NAME, &pItem))
3251             aFilterName = static_cast<const SfxStringItem*>(pItem)->GetValue();
3252     }
3253 
3254     if (aFilterProvider.isEmpty())
3255     {
3256         // This is a conventional filter type.
3257         pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4FilterName( aFilterName );
3258     }
3259     else
3260     {
3261         // This filter is from an external provider such as orcus.
3262         pImpl->m_pCustomFilter.reset(new SfxFilter(aFilterProvider, aFilterName));
3263         pImpl->m_pFilter = pImpl->m_pCustomFilter;
3264     }
3265 
3266     const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_DOC_SALVAGE, false);
3267     if( pSalvageItem )
3268     {
3269         // QUESTION: there is some treatment of Salvage in Init_Impl; align!
3270         if ( !pSalvageItem->GetValue().isEmpty() )
3271         {
3272             // if a URL is provided in SalvageItem that means that the FileName refers to a temporary file
3273             // that must be copied here
3274 
3275             const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
3276             if (!pFileNameItem) throw uno::RuntimeException();
3277             OUString aNewTempFileURL = SfxMedium::CreateTempCopyWithExt( pFileNameItem->GetValue() );
3278             if ( !aNewTempFileURL.isEmpty() )
3279             {
3280                 pImpl->m_pSet->Put( SfxStringItem( SID_FILE_NAME, aNewTempFileURL ) );
3281                 pImpl->m_pSet->ClearItem( SID_INPUTSTREAM );
3282                 pImpl->m_pSet->ClearItem( SID_STREAM );
3283                 pImpl->m_pSet->ClearItem( SID_CONTENT );
3284             }
3285             else
3286             {
3287                 SAL_WARN( "sfx.doc", "Can not create a new temporary file for crash recovery!" );
3288             }
3289         }
3290     }
3291 
3292     const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
3293     if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
3294         pImpl->m_bOriginallyLoadedReadOnly = true;
3295 
3296     const SfxStringItem* pFileNameItem = SfxItemSet::GetItem<SfxStringItem>(pImpl->m_pSet.get(), SID_FILE_NAME, false);
3297     if (!pFileNameItem) throw uno::RuntimeException();
3298     pImpl->m_aLogicName = pFileNameItem->GetValue();
3299     pImpl->m_nStorOpenMode = pImpl->m_bOriginallyLoadedReadOnly
3300         ? SFX_STREAM_READONLY : SFX_STREAM_READWRITE;
3301     Init_Impl();
3302 }
3303 
3304 
3305 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const SfxItemSet* p ) :
3306     pImpl(new SfxMedium_Impl)
3307 {
3308     OUString aType = SfxFilter::GetTypeFromStorage(rStor);
3309     pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( aType );
3310     DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
3311 
3312     Init_Impl();
3313     pImpl->xStorage = rStor;
3314     pImpl->bDisposeStorage = false;
3315 
3316     // always take BaseURL first, could be overwritten by ItemSet
3317     GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
3318     if ( p )
3319         GetItemSet()->Put( *p );
3320 }
3321 
3322 
3323 SfxMedium::SfxMedium( const uno::Reference < embed::XStorage >& rStor, const OUString& rBaseURL, const OUString &rTypeName, const SfxItemSet* p ) :
3324     pImpl(new SfxMedium_Impl)
3325 {
3326     pImpl->m_pFilter = SfxGetpApp()->GetFilterMatcher().GetFilter4EA( rTypeName );
3327     DBG_ASSERT( pImpl->m_pFilter, "No Filter for storage found!" );
3328 
3329     Init_Impl();
3330     pImpl->xStorage = rStor;
3331     pImpl->bDisposeStorage = false;
3332 
3333     // always take BaseURL first, could be overwritten by ItemSet
3334     GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, rBaseURL ) );
3335     if ( p )
3336         GetItemSet()->Put( *p );
3337 }
3338 
3339 
3340 SfxMedium::~SfxMedium()
3341 {
3342     // if there is a requirement to clean the backup this is the last possibility to do it
3343     ClearBackup_Impl();
3344 
3345     Close(/*bInDestruction*/true);
3346 
3347     if( !pImpl->bIsTemp || pImpl->m_aName.isEmpty() )
3348         return;
3349 
3350     OUString aTemp;
3351     if ( osl::FileBase::getFileURLFromSystemPath( pImpl->m_aName, aTemp )
3352          != osl::FileBase::E_None )
3353     {
3354         SAL_WARN( "sfx.doc", "Physical name not convertible!");
3355     }
3356 
3357     if ( !::utl::UCBContentHelper::Kill( aTemp ) )
3358     {
3359         SAL_WARN( "sfx.doc", "Couldn't remove temporary file!");
3360     }
3361 }
3362 
3363 const OUString& SfxMedium::GetName() const
3364 {
3365     return pImpl->m_aLogicName;
3366 }
3367 
3368 const INetURLObject& SfxMedium::GetURLObject() const
3369 {
3370     if (!pImpl->m_pURLObj)
3371     {
3372         pImpl->m_pURLObj.reset( new INetURLObject( pImpl->m_aLogicName ) );
3373         pImpl->m_pURLObj->SetMark("");
3374     }
3375 
3376     return *pImpl->m_pURLObj;
3377 }
3378 
3379 void SfxMedium::SetExpired_Impl( const DateTime& rDateTime )
3380 {
3381     pImpl->aExpireTime = rDateTime;
3382 }
3383 
3384 
3385 bool SfxMedium::IsExpired() const
3386 {
3387     return pImpl->aExpireTime.IsValidAndGregorian() && pImpl->aExpireTime < DateTime( DateTime::SYSTEM );
3388 }
3389 
3390 
3391 SfxFrame* SfxMedium::GetLoadTargetFrame() const
3392 {
3393     return pImpl->wLoadTargetFrame;
3394 }
3395 
3396 void SfxMedium::setStreamToLoadFrom(const css::uno::Reference<css::io::XInputStream>& xInputStream, bool bIsReadOnly )
3397 {
3398     pImpl->m_xInputStreamToLoadFrom = xInputStream;
3399     pImpl->m_bInputStreamIsReadOnly = bIsReadOnly;
3400 }
3401 
3402 void SfxMedium::SetLoadTargetFrame(SfxFrame* pFrame )
3403 {
3404     pImpl->wLoadTargetFrame = pFrame;
3405 }
3406 
3407 
3408 void SfxMedium::SetStorage_Impl( const uno::Reference < embed::XStorage >& rStor )
3409 {
3410     pImpl->xStorage = rStor;
3411 }
3412 
3413 
3414 SfxItemSet* SfxMedium::GetItemSet() const
3415 {
3416     // this method *must* return an ItemSet, returning NULL can cause crashes
3417     if (!pImpl->m_pSet)
3418         pImpl->m_pSet.reset( new SfxAllItemSet( SfxGetpApp()->GetPool() ) );
3419     return pImpl->m_pSet.get();
3420 }
3421 
3422 
3423 SvKeyValueIterator* SfxMedium::GetHeaderAttributes_Impl()
3424 {
3425     if( !pImpl->xAttributes.is() )
3426     {
3427         pImpl->xAttributes = SvKeyValueIteratorRef( new SvKeyValueIterator );
3428 
3429         if ( GetContent().is() )
3430         {
3431             try
3432             {
3433                 Any aAny = pImpl->aContent.getPropertyValue("MediaType");
3434                 OUString aContentType;
3435                 aAny >>= aContentType;
3436 
3437                 pImpl->xAttributes->Append( SvKeyValue( "content-type", aContentType ) );
3438             }
3439             catch ( const css::uno::Exception& )
3440             {
3441             }
3442         }
3443     }
3444 
3445     return pImpl->xAttributes.get();
3446 }
3447 
3448 css::uno::Reference< css::io::XInputStream > const &  SfxMedium::GetInputStream()
3449 {
3450     if ( !pImpl->xInputStream.is() )
3451         GetMedium_Impl();
3452     return pImpl->xInputStream;
3453 }
3454 
3455 const uno::Sequence < util::RevisionTag >& SfxMedium::GetVersionList( bool _bNoReload )
3456 {
3457     // if the medium has no name, then this medium should represent a new document and can have no version info
3458     if ( ( !_bNoReload || !pImpl->m_bVersionsAlreadyLoaded ) && !pImpl->aVersions.hasElements() &&
3459          ( !pImpl->m_aName.isEmpty() || !pImpl->m_aLogicName.isEmpty() ) && GetStorage().is() )
3460     {
3461         uno::Reference < document::XDocumentRevisionListPersistence > xReader =
3462                 document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3463         try
3464         {
3465             pImpl->aVersions = xReader->load( GetStorage() );
3466         }
3467         catch ( const uno::Exception& )
3468         {
3469         }
3470     }
3471 
3472     if ( !pImpl->m_bVersionsAlreadyLoaded )
3473         pImpl->m_bVersionsAlreadyLoaded = true;
3474 
3475     return pImpl->aVersions;
3476 }
3477 
3478 uno::Sequence < util::RevisionTag > SfxMedium::GetVersionList( const uno::Reference < embed::XStorage >& xStorage )
3479 {
3480     uno::Reference < document::XDocumentRevisionListPersistence > xReader =
3481         document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3482     try
3483     {
3484         return xReader->load( xStorage );
3485     }
3486     catch ( const uno::Exception& )
3487     {
3488     }
3489 
3490     return uno::Sequence < util::RevisionTag >();
3491 }
3492 
3493 void SfxMedium::AddVersion_Impl( util::RevisionTag& rRevision )
3494 {
3495     if ( !GetStorage().is() )
3496         return;
3497 
3498     // To determine a unique name for the stream
3499     std::vector<sal_uInt32> aLongs;
3500     sal_Int32 nLength = pImpl->aVersions.getLength();
3501     for ( const auto& rVersion : std::as_const(pImpl->aVersions) )
3502     {
3503         sal_uInt32 nVer = static_cast<sal_uInt32>( rVersion.Identifier.copy(7).toInt32());
3504         size_t n;
3505         for ( n=0; n<aLongs.size(); ++n )
3506             if ( nVer<aLongs[n] )
3507                 break;
3508 
3509         aLongs.insert( aLongs.begin()+n, nVer );
3510     }
3511 
3512     std::vector<sal_uInt32>::size_type nKey;
3513     for ( nKey=0; nKey<aLongs.size(); ++nKey )
3514         if ( aLongs[nKey] > nKey+1 )
3515             break;
3516 
3517     OUString aRevName = "Version" + OUString::number( nKey + 1 );
3518     pImpl->aVersions.realloc( nLength+1 );
3519     rRevision.Identifier = aRevName;
3520     pImpl->aVersions[nLength] = rRevision;
3521 }
3522 
3523 void SfxMedium::RemoveVersion_Impl( const OUString& rName )
3524 {
3525     if ( !pImpl->aVersions.hasElements() )
3526         return;
3527 
3528     auto pVersion = std::find_if(pImpl->aVersions.begin(), pImpl->aVersions.end(),
3529         [&rName](const auto& rVersion) { return rVersion.Identifier == rName; });
3530     if (pVersion != pImpl->aVersions.end())
3531     {
3532         auto nIndex = static_cast<sal_Int32>(std::distance(pImpl->aVersions.begin(), pVersion));
3533         comphelper::removeElementAt(pImpl->aVersions, nIndex);
3534     }
3535 }
3536 
3537 bool SfxMedium::TransferVersionList_Impl( SfxMedium const & rMedium )
3538 {
3539     if ( rMedium.pImpl->aVersions.hasElements() )
3540     {
3541         pImpl->aVersions = rMedium.pImpl->aVersions;
3542         return true;
3543     }
3544 
3545     return false;
3546 }
3547 
3548 void SfxMedium::SaveVersionList_Impl()
3549 {
3550     if ( !GetStorage().is() )
3551         return;
3552 
3553     if ( !pImpl->aVersions.hasElements() )
3554         return;
3555 
3556     uno::Reference < document::XDocumentRevisionListPersistence > xWriter =
3557              document::DocumentRevisionListPersistence::create( comphelper::getProcessComponentContext() );
3558     try
3559     {
3560         xWriter->store( GetStorage(), pImpl->aVersions );
3561     }
3562     catch ( const uno::Exception& )
3563     {
3564     }
3565 }
3566 
3567 bool SfxMedium::IsReadOnly() const
3568 {
3569     // a) ReadOnly filter can't produce read/write contents!
3570     bool bReadOnly = pImpl->m_pFilter && (pImpl->m_pFilter->GetFilterFlags() & SfxFilterFlags::OPENREADONLY);
3571 
3572     // b) if filter allow read/write contents .. check open mode of the storage
3573     if (!bReadOnly)
3574         bReadOnly = !( GetOpenMode() & StreamMode::WRITE );
3575 
3576     // c) the API can force the readonly state!
3577     if (!bReadOnly)
3578     {
3579         const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(GetItemSet(), SID_DOC_READONLY, false);
3580         if (pItem)
3581             bReadOnly = pItem->GetValue();
3582     }
3583 
3584     return bReadOnly;
3585 }
3586 
3587 bool SfxMedium::IsOriginallyReadOnly() const
3588 {
3589     return pImpl->m_bOriginallyReadOnly;
3590 }
3591 
3592 bool SfxMedium::IsOriginallyLoadedReadOnly() const
3593 {
3594     return pImpl->m_bOriginallyLoadedReadOnly;
3595 }
3596 
3597 bool SfxMedium::SetWritableForUserOnly( const OUString& aURL )
3598 {
3599     // UCB does not allow to allow write access only for the user,
3600     // use osl API
3601     bool bResult = false;
3602 
3603     ::osl::DirectoryItem aDirItem;
3604     if ( ::osl::DirectoryItem::get( aURL, aDirItem ) == ::osl::FileBase::E_None )
3605     {
3606         ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_Attributes );
3607         if ( aDirItem.getFileStatus( aFileStatus ) == osl::FileBase::E_None
3608           && aFileStatus.isValid( osl_FileStatus_Mask_Attributes ) )
3609         {
3610             sal_uInt64 nAttributes = aFileStatus.getAttributes();
3611 
3612             nAttributes &= ~(osl_File_Attribute_OwnWrite |
3613                              osl_File_Attribute_GrpWrite |
3614                              osl_File_Attribute_OthWrite |
3615                              osl_File_Attribute_ReadOnly);
3616             nAttributes |=  (osl_File_Attribute_OwnWrite |
3617                              osl_File_Attribute_OwnRead);
3618 
3619             bResult = ( osl::File::setAttributes( aURL, nAttributes ) == ::osl::FileBase::E_None );
3620         }
3621     }
3622 
3623     return bResult;
3624 }
3625 
3626 namespace
3627 {
3628 /// Get the parent directory of a temporary file for output purposes.
3629 OUString GetLogicBase(std::unique_ptr<SfxMedium_Impl> const & pImpl)
3630 {
3631     OUString aLogicBase;
3632 
3633 // In a sandboxed environment we don't want to attempt to create temporary files in the same
3634 // directory where the user has selected an output file to be stored. The sandboxed process has
3635 // permission only to create the specifically named output file in that directory.
3636 #if !HAVE_FEATURE_MACOSX_SANDBOX
3637 
3638     if (comphelper::isFileUrl(pImpl->m_aLogicName) && !pImpl->m_pInStream)
3639     {
3640         // Try to create the temp file in the same directory when storing.
3641         sal_Int32 nOffset = pImpl->m_aLogicName.lastIndexOf("/");
3642         if (nOffset != -1)
3643             aLogicBase = pImpl->m_aLogicName.copy(0, nOffset);
3644         if (aLogicBase == "file://")
3645             // Doesn't make sense.
3646             aLogicBase.clear();
3647     }
3648 
3649     if (pImpl->m_bHasEmbeddedObjects)
3650         // Embedded objects would mean a special base, ignore that.
3651         aLogicBase.clear();
3652 
3653 #endif // !HAVE_FEATURE_MACOSX_SANDBOX
3654 
3655     return aLogicBase;
3656 }
3657 }
3658 
3659 void SfxMedium::CreateTempFile( bool bReplace )
3660 {
3661     if ( pImpl->pTempFile )
3662     {
3663         if ( !bReplace )
3664             return;
3665 
3666         pImpl->pTempFile.reset();
3667         pImpl->m_aName.clear();
3668     }
3669 
3670     OUString aLogicBase = GetLogicBase(pImpl);
3671     pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) );
3672     pImpl->pTempFile->EnableKillingFile();
3673     pImpl->m_aName = pImpl->pTempFile->GetFileName();
3674     OUString aTmpURL = pImpl->pTempFile->GetURL();
3675     if ( pImpl->m_aName.isEmpty() || aTmpURL.isEmpty() )
3676     {
3677         SetError(ERRCODE_IO_CANTWRITE);
3678         return;
3679     }
3680 
3681     if ( !(pImpl->m_nStorOpenMode & StreamMode::TRUNC) )
3682     {
3683         bool bTransferSuccess = false;
3684 
3685         if ( GetContent().is()
3686           && GetURLObject().GetProtocol() == INetProtocol::File
3687           && ::utl::UCBContentHelper::IsDocument( GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
3688         {
3689             // if there is already such a document, we should copy it
3690             // if it is a file system use OS copy process
3691             try
3692             {
3693                 uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
3694                 INetURLObject aTmpURLObj( aTmpURL );
3695                 OUString aFileName = aTmpURLObj.getName( INetURLObject::LAST_SEGMENT,
3696                                                                 true,
3697                                                                 INetURLObject::DecodeMechanism::WithCharset );
3698                 if ( !aFileName.isEmpty() && aTmpURLObj.removeSegment() )
3699                 {
3700                     ::ucbhelper::Content aTargetContent( aTmpURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
3701                     OUString sMimeType = pImpl->getFilterMimeType();
3702                     aTargetContent.transferContent( pImpl->aContent, ::ucbhelper::InsertOperation::Copy, aFileName, NameClash::OVERWRITE, sMimeType );
3703                     SetWritableForUserOnly( aTmpURL );
3704                     bTransferSuccess = true;
3705                 }
3706             }
3707             catch( const uno::Exception& )
3708             {}
3709 
3710             if ( bTransferSuccess )
3711             {
3712                 CloseOutStream();
3713                 CloseInStream();
3714             }
3715         }
3716 
3717         if ( !bTransferSuccess && pImpl->m_pInStream )
3718         {
3719             // the case when there is no URL-access available or this is a remote protocol
3720             // but there is an input stream
3721             GetOutStream();
3722             if ( pImpl->m_pOutStream )
3723             {
3724                 std::unique_ptr<char[]> pBuf(new char [8192]);
3725                 ErrCode      nErr = ERRCODE_NONE;
3726 
3727                 pImpl->m_pInStream->Seek(0);
3728                 pImpl->m_pOutStream->Seek(0);
3729 
3730                 while( !pImpl->m_pInStream->eof() && nErr == ERRCODE_NONE )
3731                 {
3732                     sal_uInt32 nRead = pImpl->m_pInStream->ReadBytes(pBuf.get(), 8192);
3733                     nErr = pImpl->m_pInStream->GetError();
3734                     pImpl->m_pOutStream->WriteBytes( pBuf.get(), nRead );
3735                 }
3736 
3737                 bTransferSuccess = true;
3738                 CloseInStream();
3739             }
3740             CloseOutStream_Impl();
3741         }
3742         else
3743         {
3744             // Quite strange design, but currently it is expected that in this case no transfer happens
3745             // TODO/LATER: get rid of this inconsistent part of the call design
3746             bTransferSuccess = true;
3747             CloseInStream();
3748         }
3749 
3750         if ( !bTransferSuccess )
3751         {
3752             SetError(ERRCODE_IO_CANTWRITE);
3753             return;
3754         }
3755     }
3756 
3757     CloseStorage();
3758 }
3759 
3760 
3761 void SfxMedium::CreateTempFileNoCopy()
3762 {
3763     // this call always replaces the existing temporary file
3764     pImpl->pTempFile.reset();
3765 
3766     OUString aLogicBase = GetLogicBase(pImpl);
3767     pImpl->pTempFile.reset( new ::utl::TempFile(aLogicBase.isEmpty() ? nullptr : &aLogicBase) );
3768     pImpl->pTempFile->EnableKillingFile();
3769     pImpl->m_aName = pImpl->pTempFile->GetFileName();
3770     if ( pImpl->m_aName.isEmpty() )
3771     {
3772         SetError(ERRCODE_IO_CANTWRITE);
3773         return;
3774     }
3775 
3776     CloseOutStream_Impl();
3777     CloseStorage();
3778 }
3779 
3780 bool SfxMedium::SignDocumentContentUsingCertificate(bool bHasValidDocumentSignature,
3781                                                     const Reference<XCertificate>& xCertificate)
3782 {
3783     bool bChanges = false;
3784 
3785     if (IsOpen() || GetError())
3786     {
3787         SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
3788         return bChanges;
3789     }
3790 
3791     // The component should know if there was a valid document signature, since
3792     // it should show a warning in this case
3793     OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
3794     uno::Reference< security::XDocumentDigitalSignatures > xSigner(
3795         security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
3796             comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
3797 
3798     uno::Reference< embed::XStorage > xWriteableZipStor;
3799 
3800     // we can reuse the temporary file if there is one already
3801     CreateTempFile( false );
3802     GetMedium_Impl();
3803 
3804     try
3805     {
3806         if ( !pImpl->xStream.is() )
3807             throw uno::RuntimeException();
3808 
3809         bool bODF = GetFilter()->IsOwnFormat();
3810         try
3811         {
3812             xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
3813         }
3814         catch (const io::IOException&)
3815         {
3816             if (bODF)
3817             {
3818                 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
3819             }
3820         }
3821 
3822         if ( !xWriteableZipStor.is() && bODF )
3823             throw uno::RuntimeException();
3824 
3825         uno::Reference< embed::XStorage > xMetaInf;
3826         if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
3827         {
3828             xMetaInf = xWriteableZipStor->openStorageElement(
3829                                             "META-INF",
3830                                             embed::ElementModes::READWRITE );
3831             if ( !xMetaInf.is() )
3832                 throw uno::RuntimeException();
3833         }
3834 
3835         {
3836             if (xMetaInf.is())
3837             {
3838                 // ODF.
3839                 uno::Reference< io::XStream > xStream;
3840                 if (GetFilter() && GetFilter()->IsOwnFormat())
3841                     xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
3842 
3843                 bool bSuccess = xSigner->signDocumentWithCertificate(xCertificate, GetZipStorageToSign_Impl(), xStream);
3844 
3845                 if (bSuccess)
3846                 {
3847                     uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
3848                     xTransact->commit();
3849                     xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
3850                     xTransact->commit();
3851 
3852                     // the temporary file has been written, commit it to the original file
3853                     Commit();
3854                     bChanges = true;
3855                 }
3856             }
3857             else if (xWriteableZipStor.is())
3858             {
3859                 // OOXML.
3860                 uno::Reference<io::XStream> xStream;
3861 
3862                     // We need read-write to be able to add the signature relation.
3863                 bool bSuccess =xSigner->signDocumentWithCertificate(
3864                         xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
3865 
3866                 if (bSuccess)
3867                 {
3868                     uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
3869                     xTransact->commit();
3870 
3871                     // the temporary file has been written, commit it to the original file
3872                     Commit();
3873                     bChanges = true;
3874                 }
3875             }
3876             else
3877             {
3878                 // Something not ZIP based: e.g. PDF.
3879                 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
3880                 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
3881                 if (xSigner->signDocumentWithCertificate(xCertificate, uno::Reference<embed::XStorage>(), xStream))
3882                     bChanges = true;
3883             }
3884         }
3885     }
3886     catch ( const uno::Exception& )
3887     {
3888         SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
3889     }
3890 
3891     CloseAndRelease();
3892 
3893     ResetError();
3894 
3895     return bChanges;
3896 }
3897 
3898 bool SfxMedium::SignContents_Impl(weld::Window* pDialogParent,
3899                                   bool bSignScriptingContent,
3900                                   bool bHasValidDocumentSignature,
3901                                   const OUString& aSignatureLineId,
3902                                   const Reference<XCertificate>& xCert,
3903                                   const Reference<XGraphic>& xValidGraphic,
3904                                   const Reference<XGraphic>& xInvalidGraphic,
3905                                   const OUString& aComment)
3906 {
3907     bool bChanges = false;
3908 
3909     if (IsOpen() || GetError())
3910     {
3911         SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
3912         return bChanges;
3913     }
3914 
3915     // The component should know if there was a valid document signature, since
3916     // it should show a warning in this case
3917     OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
3918     uno::Reference< security::XDocumentDigitalSignatures > xSigner(
3919         security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
3920             comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
3921     if (pDialogParent)
3922         xSigner->setParentWindow(pDialogParent->GetXWindow());
3923 
3924     uno::Reference< embed::XStorage > xWriteableZipStor;
3925 
3926     // we can reuse the temporary file if there is one already
3927     CreateTempFile( false );
3928     GetMedium_Impl();
3929 
3930     try
3931     {
3932         if ( !pImpl->xStream.is() )
3933             throw uno::RuntimeException();
3934 
3935         bool bODF = GetFilter()->IsOwnFormat();
3936         try
3937         {
3938             xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
3939         }
3940         catch (const io::IOException&)
3941         {
3942             if (bODF)
3943             {
3944                 TOOLS_WARN_EXCEPTION("sfx.doc", "ODF stream is not a zip storage");
3945             }
3946         }
3947 
3948         if ( !xWriteableZipStor.is() && bODF )
3949             throw uno::RuntimeException();
3950 
3951         uno::Reference< embed::XStorage > xMetaInf;
3952         if (xWriteableZipStor.is() && xWriteableZipStor->hasByName("META-INF"))
3953         {
3954             xMetaInf = xWriteableZipStor->openStorageElement(
3955                                             "META-INF",
3956                                             embed::ElementModes::READWRITE );
3957             if ( !xMetaInf.is() )
3958                 throw uno::RuntimeException();
3959         }
3960 
3961         if ( bSignScriptingContent )
3962         {
3963             // If the signature has already the document signature it will be removed
3964             // after the scripting signature is inserted.
3965             uno::Reference< io::XStream > xStream(
3966                 xMetaInf->openStreamElement( xSigner->getScriptingContentSignatureDefaultStreamName(),
3967                                                 embed::ElementModes::READWRITE ),
3968                 uno::UNO_SET_THROW );
3969 
3970             if ( xSigner->signScriptingContent( GetZipStorageToSign_Impl(), xStream ) )
3971             {
3972                 // remove the document signature if any
3973                 OUString aDocSigName = xSigner->getDocumentContentSignatureDefaultStreamName();
3974                 if ( !aDocSigName.isEmpty() && xMetaInf->hasByName( aDocSigName ) )
3975                     xMetaInf->removeElement( aDocSigName );
3976 
3977                 uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
3978                 xTransact->commit();
3979                 xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
3980                 xTransact->commit();
3981 
3982                 // the temporary file has been written, commit it to the original file
3983                 Commit();
3984                 bChanges = true;
3985             }
3986         }
3987         else
3988         {
3989             if (xMetaInf.is())
3990             {
3991                 // ODF.
3992                 uno::Reference< io::XStream > xStream;
3993                 if (GetFilter() && GetFilter()->IsOwnFormat())
3994                     xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
3995 
3996                 bool bSuccess = false;
3997                 if (xCert.is())
3998                     bSuccess = xSigner->signSignatureLine(
3999                         GetZipStorageToSign_Impl(), xStream, aSignatureLineId, xCert,
4000                         xValidGraphic, xInvalidGraphic, aComment);
4001                 else
4002                     bSuccess = xSigner->signDocumentContent(GetZipStorageToSign_Impl(),
4003                                                             xStream);
4004 
4005                 if (bSuccess)
4006                 {
4007                     uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
4008                     xTransact->commit();
4009                     xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
4010                     xTransact->commit();
4011 
4012                     // the temporary file has been written, commit it to the original file
4013                     Commit();
4014                     bChanges = true;
4015                 }
4016             }
4017             else if (xWriteableZipStor.is())
4018             {
4019                 // OOXML.
4020                 uno::Reference<io::XStream> xStream;
4021 
4022                 bool bSuccess = false;
4023                 if (xCert.is())
4024                 {
4025                     bSuccess = xSigner->signSignatureLine(
4026                         GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream, aSignatureLineId,
4027                         xCert, xValidGraphic, xInvalidGraphic, aComment);
4028                 }
4029                 else
4030                 {
4031                     // We need read-write to be able to add the signature relation.
4032                     bSuccess =xSigner->signDocumentContent(
4033                         GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
4034                 }
4035 
4036                 if (bSuccess)
4037                 {
4038                     uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
4039                     xTransact->commit();
4040 
4041                     // the temporary file has been written, commit it to the original file
4042                     Commit();
4043                     bChanges = true;
4044                 }
4045             }
4046             else
4047             {
4048                 // Something not ZIP based: e.g. PDF.
4049                 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
4050                 uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
4051                 if (xSigner->signDocumentContent(uno::Reference<embed::XStorage>(), xStream))
4052                     bChanges = true;
4053             }
4054         }
4055     }
4056     catch ( const uno::Exception& )
4057     {
4058         SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
4059     }
4060 
4061     CloseAndRelease();
4062 
4063     ResetError();
4064 
4065     return bChanges;
4066 }
4067 
4068 
4069 SignatureState SfxMedium::GetCachedSignatureState_Impl() const
4070 {
4071     return pImpl->m_nSignatureState;
4072 }
4073 
4074 
4075 void SfxMedium::SetCachedSignatureState_Impl( SignatureState nState )
4076 {
4077     pImpl->m_nSignatureState = nState;
4078 }
4079 
4080 void SfxMedium::SetHasEmbeddedObjects(bool bHasEmbeddedObjects)
4081 {
4082     pImpl->m_bHasEmbeddedObjects = bHasEmbeddedObjects;
4083 }
4084 
4085 bool SfxMedium::HasStorage_Impl() const
4086 {
4087     return pImpl->xStorage.is();
4088 }
4089 
4090 bool SfxMedium::IsOpen() const
4091 {
4092     return pImpl->m_pInStream || pImpl->m_pOutStream || pImpl->xStorage.is();
4093 }
4094 
4095 OUString SfxMedium::CreateTempCopyWithExt( const OUString& aURL )
4096 {
4097     OUString aResult;
4098 
4099     if ( !aURL.isEmpty() )
4100     {
4101         sal_Int32 nPrefixLen = aURL.lastIndexOf( '.' );
4102         OUString aExt = ( nPrefixLen == -1 ) ? OUString() :  aURL.copy( nPrefixLen );
4103 
4104         OUString aNewTempFileURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
4105         if ( !aNewTempFileURL.isEmpty() )
4106         {
4107             INetURLObject aSource( aURL );
4108             INetURLObject aDest( aNewTempFileURL );
4109             OUString aFileName = aDest.getName( INetURLObject::LAST_SEGMENT,
4110                                                         true,
4111                                                         INetURLObject::DecodeMechanism::WithCharset );
4112             if ( !aFileName.isEmpty() && aDest.removeSegment() )
4113             {
4114                 try
4115                 {
4116                     uno::Reference< css::ucb::XCommandEnvironment > xComEnv;
4117                     ::ucbhelper::Content aTargetContent( aDest.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
4118                     ::ucbhelper::Content aSourceContent( aSource.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xComEnv, comphelper::getProcessComponentContext() );
4119                     aTargetContent.transferContent( aSourceContent,
4120                                                         ::ucbhelper::InsertOperation::Copy,
4121                                                         aFileName,
4122                                                         NameClash::OVERWRITE );
4123                     aResult = aNewTempFileURL;
4124                 }
4125                 catch( const uno::Exception& )
4126                 {}
4127             }
4128         }
4129     }
4130 
4131     return aResult;
4132 }
4133 
4134 bool SfxMedium::CallApproveHandler(const uno::Reference< task::XInteractionHandler >& xHandler, const uno::Any& rRequest, bool bAllowAbort)
4135 {
4136     bool bResult = false;
4137 
4138     if ( xHandler.is() )
4139     {
4140         try
4141         {
4142             uno::Sequence< uno::Reference< task::XInteractionContinuation > > aContinuations( bAllowAbort ? 2 : 1 );
4143 
4144             ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove );
4145             aContinuations[ 0 ] = pApprove.get();
4146 
4147             if ( bAllowAbort )
4148             {
4149                 ::rtl::Reference< ::comphelper::OInteractionAbort > pAbort( new ::comphelper::OInteractionAbort );
4150                 aContinuations[ 1 ] = pAbort.get();
4151             }
4152 
4153             xHandler->handle(::framework::InteractionRequest::CreateRequest(rRequest, aContinuations));
4154             bResult = pApprove->wasSelected();
4155         }
4156         catch( const Exception& )
4157         {
4158         }
4159     }
4160 
4161     return bResult;
4162 }
4163 
4164 OUString SfxMedium::SwitchDocumentToTempFile()
4165 {
4166     // the method returns empty string in case of failure
4167     OUString aResult;
4168     OUString aOrigURL = pImpl->m_aLogicName;
4169 
4170     if ( !aOrigURL.isEmpty() )
4171     {
4172         sal_Int32 nPrefixLen = aOrigURL.lastIndexOf( '.' );
4173         OUString const aExt = (nPrefixLen == -1)
4174                                 ? OUString()
4175                                 : aOrigURL.copy(nPrefixLen);
4176         OUString aNewURL = ::utl::TempFile( OUString(), true, &aExt ).GetURL();
4177 
4178         // TODO/LATER: In future the aLogicName should be set to shared folder URL
4179         //             and a temporary file should be created. Transport_Impl should be impossible then.
4180         if ( !aNewURL.isEmpty() )
4181         {
4182             uno::Reference< embed::XStorage > xStorage = GetStorage();
4183             uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
4184 
4185             if ( xOptStorage.is() )
4186             {
4187                 // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4188                 CanDisposeStorage_Impl( false );
4189                 Close();
4190                 SetPhysicalName_Impl( OUString() );
4191                 SetName( aNewURL );
4192 
4193                 // remove the readonly state
4194                 bool bWasReadonly = false;
4195                 pImpl->m_nStorOpenMode = SFX_STREAM_READWRITE;
4196                 const SfxBoolItem* pReadOnlyItem = SfxItemSet::GetItem<SfxBoolItem>(pImpl->m_pSet.get(), SID_DOC_READONLY, false);
4197                 if ( pReadOnlyItem && pReadOnlyItem->GetValue() )
4198                     bWasReadonly = true;
4199                 GetItemSet()->ClearItem( SID_DOC_READONLY );
4200 
4201                 GetMedium_Impl();
4202                 LockOrigFileOnDemand( false, false );
4203                 CreateTempFile();
4204                 GetMedium_Impl();
4205 
4206                 if ( pImpl->xStream.is() )
4207                 {
4208                     try
4209                     {
4210                         xOptStorage->writeAndAttachToStream( pImpl->xStream );
4211                         pImpl->xStorage = xStorage;
4212                         aResult = aNewURL;
4213                     }
4214                     catch( const uno::Exception& )
4215                     {}
4216                 }
4217 
4218                 if ( aResult.isEmpty() )
4219                 {
4220                     Close();
4221                     SetPhysicalName_Impl( OUString() );
4222                     SetName( aOrigURL );
4223                     if ( bWasReadonly )
4224                     {
4225                         // set the readonly state back
4226                         pImpl->m_nStorOpenMode = SFX_STREAM_READONLY;
4227                         GetItemSet()->Put( SfxBoolItem(SID_DOC_READONLY, true));
4228                     }
4229                     GetMedium_Impl();
4230                     pImpl->xStorage = xStorage;
4231                 }
4232             }
4233         }
4234     }
4235 
4236     return aResult;
4237 }
4238 
4239 bool SfxMedium::SwitchDocumentToFile( const OUString& aURL )
4240 {
4241     // the method is only for storage based documents
4242     bool bResult = false;
4243     OUString aOrigURL = pImpl->m_aLogicName;
4244 
4245     if ( !aURL.isEmpty() && !aOrigURL.isEmpty() )
4246     {
4247         uno::Reference< embed::XStorage > xStorage = GetStorage();
4248         uno::Reference< embed::XOptimizedStorage > xOptStorage( xStorage, uno::UNO_QUERY );
4249 
4250         // TODO/LATER: reuse the pImpl->pTempFile if it already exists
4251         CanDisposeStorage_Impl( false );
4252         Close();
4253         SetPhysicalName_Impl( OUString() );
4254         SetName( aURL );
4255 
4256         // open the temporary file based document
4257         GetMedium_Impl();
4258         LockOrigFileOnDemand( false, false );
4259         CreateTempFile();
4260         GetMedium_Impl();
4261 
4262         if ( pImpl->xStream.is() )
4263         {
4264             try
4265             {
4266                 uno::Reference< io::XTruncate > xTruncate( pImpl->xStream, uno::UNO_QUERY_THROW );
4267                 xTruncate->truncate();
4268                 if ( xOptStorage.is() )
4269                     xOptStorage->writeAndAttachToStream( pImpl->xStream );
4270                 pImpl->xStorage = xStorage;
4271                 bResult = true;
4272             }
4273             catch( const uno::Exception& )
4274             {}
4275         }
4276 
4277         if ( !bResult )
4278         {
4279             Close();
4280             SetPhysicalName_Impl( OUString() );
4281             SetName( aOrigURL );
4282             GetMedium_Impl();
4283             pImpl->xStorage = xStorage;
4284         }
4285     }
4286 
4287     return bResult;
4288 }
4289 
4290 void SfxMedium::SetInCheckIn( bool bInCheckIn )
4291 {
4292     pImpl->m_bInCheckIn = bInCheckIn;
4293 }
4294 
4295 bool SfxMedium::IsInCheckIn( ) const
4296 {
4297     return pImpl->m_bInCheckIn;
4298 }
4299 
4300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4301