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