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