Coverage Report

Created: 2026-09-28 10:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */