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