Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/sfx2/source/appl/fileobj.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <tools/urlobj.hxx>
21
#include <tools/stream.hxx>
22
#include <sot/formats.hxx>
23
#include <sal/log.hxx>
24
#include <sfx2/lnkbase.hxx>
25
#include <sfx2/filedlghelper.hxx>
26
#include <sot/exchange.hxx>
27
#include <com/sun/star/uno/Any.hxx>
28
#include <com/sun/star/uno/Sequence.hxx>
29
#include <sfx2/docfac.hxx>
30
#include <com/sun/star/document/XTypeDetection.hpp>
31
#include <com/sun/star/container/XNameAccess.hpp>
32
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
33
#include <unotools/mediadescriptor.hxx>
34
#include <comphelper/processfactory.hxx>
35
#include <sfx2/linkmgr.hxx>
36
#include <sfx2/opengrf.hxx>
37
#include <sfx2/sfxresid.hxx>
38
#include <sfx2/objsh.hxx>
39
#include "fileobj.hxx"
40
#include <sfx2/strings.hrc>
41
#include <vcl/svapp.hxx>
42
43
enum class SvFileObjectType
44
{
45
    Text = 1, Graphic = 2, Object = 3
46
};
47
48
SvFileObject::SvFileObject()
49
89.1k
    : nPostUserEventId(nullptr)
50
89.1k
    , nType(SvFileObjectType::Text)
51
89.1k
    , bLoadAgain(true)
52
89.1k
    , bSynchron(false)
53
89.1k
    , bLoadError(false)
54
89.1k
    , bWaitForData(false)
55
89.1k
    , bDataReady(false)
56
89.1k
    , bClearMedium(false)
57
89.1k
    , bStateChangeCalled(false)
58
89.1k
{
59
89.1k
}
60
61
SvFileObject::~SvFileObject()
62
89.0k
{
63
89.0k
    if (xMed.is())
64
0
    {
65
0
        xMed->SetDoneLink( Link<void*,void>() );
66
0
        xMed.clear();
67
0
    }
68
89.0k
    if (nPostUserEventId)
69
0
        Application::RemoveUserEvent(nPostUserEventId);
70
89.0k
}
71
72
bool SvFileObject::GetData( css::uno::Any & rData,
73
                                const OUString & rMimeType,
74
                                bool /*bGetSynchron*/ )
75
26.7k
{
76
26.7k
    SotClipboardFormatId nFmt = SotExchange::RegisterFormatMimeType( rMimeType );
77
26.7k
    switch( nType )
78
26.7k
    {
79
9.25k
    case SvFileObjectType::Text:
80
9.25k
        if( SotClipboardFormatId::SIMPLE_FILE == nFmt )
81
9.25k
        {
82
            // The media in the application must be opened to lookup the
83
            // relative file links!! This is done through the link manager
84
            // of the Storage.
85
9.25k
            rData <<= sFileNm;
86
9.25k
        }
87
9.25k
        break;
88
89
17.4k
    case SvFileObjectType::Graphic:
90
17.4k
        if (SotClipboardFormatId::GDIMETAFILE == nFmt
91
17.4k
         || SotClipboardFormatId::BITMAP == nFmt
92
17.4k
         || SotClipboardFormatId::SVXB == nFmt)
93
17.4k
        {
94
17.4k
            rData <<= sFileNm;
95
17.4k
        }
96
17.4k
        break;
97
0
    case SvFileObjectType::Object:
98
        // TODO/LATER: possibility to insert a new object
99
0
        rData <<= sFileNm;
100
0
        break;
101
26.7k
    }
102
26.7k
    return true/*0 != aTypeList.Count()*/;
103
26.7k
}
104
105
bool SvFileObject::Connect( sfx2::SvBaseLink* pLink )
106
89.1k
{
107
89.1k
    if( !pLink || !pLink->GetLinkManager() )
108
0
        return false;
109
110
    // Test if not another link of the same connection already exists
111
89.1k
    sfx2::LinkManager::GetDisplayNames( pLink, nullptr, &sFileNm, nullptr, &sFilter );
112
113
89.1k
    if( sfx2::SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() )
114
78.8k
    {
115
78.8k
        SfxObjectShellRef pShell = pLink->GetLinkManager()->GetPersist();
116
78.8k
        if( pShell.is() )
117
78.8k
        {
118
78.8k
            if( pShell->IsAbortingImport() )
119
0
                return false;
120
121
78.8k
            if( pShell->GetMedium() )
122
78.4k
                sReferer = pShell->GetMedium()->GetName();
123
78.8k
        }
124
78.8k
    }
125
126
89.1k
    switch( pLink->GetObjType() )
127
89.1k
    {
128
78.8k
    case sfx2::SvBaseLinkObjectType::ClientGraphic:
129
78.8k
        nType = SvFileObjectType::Graphic;
130
78.8k
        bSynchron = pLink->IsSynchron();
131
78.8k
        break;
132
133
10.2k
    case sfx2::SvBaseLinkObjectType::ClientFile:
134
10.2k
        nType = SvFileObjectType::Text;
135
10.2k
        break;
136
137
0
    case sfx2::SvBaseLinkObjectType::ClientOle:
138
0
        nType = SvFileObjectType::Object;
139
        // TODO/LATER: introduce own type to be used for exchanging
140
0
        break;
141
142
0
    default:
143
0
        return false;
144
89.1k
    }
145
146
89.1k
    SetUpdateTimeout( 0 );
147
148
    // and now register by this or other found Pseudo-Object
149
89.1k
    AddDataAdvise( pLink, SotExchange::GetFormatMimeType( pLink->GetContentType()), 0 );
150
89.1k
    return true;
151
89.1k
}
152
153
bool SvFileObject::LoadFile_Impl()
154
0
{
155
    // We are still at Loading!!
156
0
    if( bWaitForData || !bLoadAgain || xMed.is() )
157
0
        return false;
158
159
    // at the moment on the current DocShell
160
0
    xMed = new SfxMedium( sFileNm, sReferer, StreamMode::STD_READ );
161
0
    SvLinkSource::StreamToLoadFrom aStreamToLoadFrom =
162
0
        getStreamToLoadFrom();
163
0
    xMed->setStreamToLoadFrom(
164
0
        aStreamToLoadFrom.m_xInputStreamToLoadFrom,
165
0
        aStreamToLoadFrom.m_bIsReadOnly);
166
167
0
    if( !bSynchron )
168
0
    {
169
0
        bLoadAgain = bDataReady = false;
170
0
        bWaitForData = true;
171
172
0
        tools::SvRef<SfxMedium> xTmpMed = xMed;
173
0
        xMed->Download( LINK( this, SvFileObject, LoadGrfReady_Impl ) );
174
175
0
        bClearMedium = !xMed.is();
176
0
        if( bClearMedium )
177
0
            xMed = std::move(xTmpMed);  // If already finished in Download
178
0
        return bDataReady;
179
0
    }
180
181
0
    bWaitForData = true;
182
0
    bDataReady = false;
183
0
    xMed->Download();
184
0
    bLoadAgain = !xMed->IsRemote();
185
0
    bWaitForData = false;
186
187
    // Graphic is finished, also send DataChanged of the Status change:
188
0
    SendStateChg_Impl( xMed->GetInStream() && xMed->GetInStream()->GetError()
189
0
                        ? sfx2::LinkManager::STATE_LOAD_ERROR : sfx2::LinkManager::STATE_LOAD_OK );
190
0
    return true;
191
0
}
192
193
194
/** detect the filter of the given file
195
196
    @param _rURL
197
        specifies the URL of the file which filter is to detected.<br/>
198
        If the URL doesn't denote a valid (existent and accessible) file, the
199
        request is silently dropped.
200
*/
201
static OUString impl_getFilter( const OUString& _rURL )
202
0
{
203
0
    OUString sFilter;
204
0
    if ( _rURL.isEmpty() )
205
0
        return sFilter;
206
207
0
    try
208
0
    {
209
0
        css::uno::Reference< css::document::XTypeDetection > xTypeDetection(
210
0
            ::comphelper::getProcessServiceFactory()->createInstance( u"com.sun.star.document.TypeDetection"_ustr ),
211
0
            css::uno::UNO_QUERY );
212
0
        if ( xTypeDetection.is() )
213
0
        {
214
0
            utl::MediaDescriptor aDescr;
215
0
            aDescr[ utl::MediaDescriptor::PROP_URL ] <<= _rURL;
216
0
            css::uno::Sequence< css::beans::PropertyValue > aDescrList =
217
0
                aDescr.getAsConstPropertyValueList();
218
0
            OUString sType = xTypeDetection->queryTypeByDescriptor( aDescrList, true );
219
0
            if ( !sType.isEmpty() )
220
0
            {
221
                // Honor a selected/detected filter.
222
0
                for (const auto& rDescr : aDescrList)
223
0
                {
224
0
                    if (rDescr.Name == "FilterName")
225
0
                    {
226
0
                        if (rDescr.Value >>= sFilter)
227
0
                            break;
228
0
                    }
229
0
                }
230
0
                if (sFilter.isEmpty())
231
0
                {
232
0
                    css::uno::Reference< css::container::XNameAccess > xTypeCont( xTypeDetection,
233
0
                            css::uno::UNO_QUERY );
234
0
                    if ( xTypeCont.is() )
235
0
                    {
236
                        /* XXX: for fdo#69948 scenario the sequence returned by
237
                         * getByName() contains an empty PreferredFilter
238
                         * property value (since? expected?) */
239
0
                        ::comphelper::SequenceAsHashMap lTypeProps( xTypeCont->getByName( sType ) );
240
0
                        sFilter = lTypeProps.getUnpackedValueOrDefault(
241
0
                                u"PreferredFilter"_ustr, OUString() );
242
0
                    }
243
0
                }
244
0
            }
245
0
        }
246
0
    }
247
0
    catch( const css::uno::Exception& )
248
0
    {
249
0
    }
250
251
0
    return sFilter;
252
0
}
253
254
void SvFileObject::Edit(weld::Window* pParent, sfx2::SvBaseLink* pLink, const Link<const OUString&, void>& rEndEditHdl)
255
0
{
256
0
    aEndEditLink = rEndEditHdl;
257
0
    OUString sFile, sRange, sTmpFilter;
258
0
    if( !pLink || !pLink->GetLinkManager() )
259
0
        return;
260
261
0
    sfx2::LinkManager::GetDisplayNames( pLink, nullptr, &sFile, &sRange, &sTmpFilter );
262
263
0
    switch( pLink->GetObjType() )
264
0
    {
265
0
        case sfx2::SvBaseLinkObjectType::ClientGraphic:
266
0
        {
267
0
            nType = SvFileObjectType::Graphic;       // If not set already
268
269
0
            SvxOpenGraphicDialog aDlg(SfxResId(RID_SVXSTR_EDITGRFLINK), pParent);
270
0
            aDlg.EnableLink(false);
271
0
            aDlg.SetPath( sFile, true );
272
0
            aDlg.SetCurrentFilter( sTmpFilter );
273
274
0
            if( !aDlg.Execute() )
275
0
            {
276
0
                sFile = aDlg.GetPath()
277
0
                    + OUStringChar(sfx2::cTokenSeparator)
278
0
                    + OUStringChar(sfx2::cTokenSeparator)
279
0
                    + aDlg.GetDetectedFilter();
280
281
0
                aEndEditLink.Call( sFile );
282
0
            }
283
0
            else
284
0
                sFile.clear();
285
0
        }
286
0
        break;
287
288
0
        case sfx2::SvBaseLinkObjectType::ClientOle:
289
0
        {
290
0
            nType = SvFileObjectType::Object; // if not set already
291
292
0
            ::sfx2::FileDialogHelper & rFileDlg =
293
0
                pLink->GetInsertFileDialog( OUString() );
294
0
            rFileDlg.SetContext(sfx2::FileDialogHelper::LinkClientOLE);
295
0
            rFileDlg.StartExecuteModal(
296
0
                    LINK( this, SvFileObject, DialogClosedHdl ) );
297
0
        }
298
0
        break;
299
300
0
        case sfx2::SvBaseLinkObjectType::ClientFile:
301
0
        {
302
0
            nType = SvFileObjectType::Text; // if not set already
303
304
0
            OUString sFactory;
305
0
            SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
306
0
            if ( pShell )
307
0
                sFactory = pShell->GetFactory().GetFactoryName();
308
309
0
            ::sfx2::FileDialogHelper & rFileDlg =
310
0
                pLink->GetInsertFileDialog(sFactory);
311
0
            rFileDlg.SetContext(sfx2::FileDialogHelper::LinkClientFile);
312
0
            rFileDlg.StartExecuteModal(
313
0
                    LINK( this, SvFileObject, DialogClosedHdl ) );
314
0
        }
315
0
        break;
316
317
0
        default:
318
0
            sFile.clear();
319
0
    }
320
0
}
321
322
IMPL_LINK_NOARG( SvFileObject, LoadGrfReady_Impl, void*, void )
323
0
{
324
    // When we come from here there it can not be an error no more.
325
0
    bLoadError = false;
326
0
    bWaitForData = false;
327
328
0
    if( !bDataReady )
329
0
    {
330
        // Graphic is finished, also send DataChanged from Status change
331
0
        bDataReady = true;
332
0
        SendStateChg_Impl( sfx2::LinkManager::STATE_LOAD_OK );
333
334
        // and then send the data again
335
0
        NotifyDataChanged();
336
0
    }
337
338
0
    if( bDataReady )
339
0
    {
340
0
        bLoadAgain = true;
341
0
        if( xMed.is() )
342
0
        {
343
0
            xMed->SetDoneLink( Link<void*,void>() );
344
0
            mxDelMed = xMed;
345
0
            nPostUserEventId = Application::PostUserEvent(
346
0
                        LINK( this, SvFileObject, DelMedium_Impl ));
347
0
            xMed.clear();
348
0
        }
349
0
    }
350
0
}
351
352
IMPL_LINK_NOARG( SvFileObject, DelMedium_Impl, void*, void )
353
0
{
354
0
    nPostUserEventId = nullptr;
355
0
    mxDelMed.clear();
356
0
}
357
358
IMPL_LINK( SvFileObject, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
359
0
{
360
0
    OUString sFile;
361
362
0
    if ( SvFileObjectType::Text == nType || SvFileObjectType::Object == nType )
363
0
    {
364
0
        if ( _pFileDlg && _pFileDlg->GetError() == ERRCODE_NONE )
365
0
        {
366
0
            OUString sURL( _pFileDlg->GetPath() );
367
0
            sFile = sURL + OUStringChar(sfx2::cTokenSeparator)
368
0
                + OUStringChar(sfx2::cTokenSeparator)
369
0
                + impl_getFilter( sURL );
370
0
        }
371
0
    }
372
0
    else
373
0
    {
374
0
        SAL_WARN( "sfx.appl", "SvFileObject::DialogClosedHdl(): wrong file type" );
375
0
    }
376
377
0
    aEndEditLink.Call( sFile );
378
0
}
379
380
/*
381
    The method determines whether the data-object can be read from a DDE.
382
*/
383
bool SvFileObject::IsPending() const
384
0
{
385
0
    return SvFileObjectType::Graphic == nType && !bLoadError && bWaitForData;
386
0
}
387
388
bool SvFileObject::IsDataComplete() const
389
0
{
390
0
    bool bRet = false;
391
0
    if( SvFileObjectType::Graphic != nType )
392
0
        bRet = true;
393
0
    else if( !bLoadError && !bWaitForData )
394
0
    {
395
0
        SvFileObject* pThis = const_cast<SvFileObject*>(this);
396
0
        if( bDataReady ||
397
0
            ( bSynchron && pThis->LoadFile_Impl() && xMed.is() ) )
398
0
            bRet = true;
399
0
        else
400
0
        {
401
0
            INetURLObject aUrl( sFileNm );
402
0
            if( aUrl.HasError() ||
403
0
                INetProtocol::NotValid == aUrl.GetProtocol() )
404
0
                bRet = true;
405
0
        }
406
0
    }
407
0
    return bRet;
408
0
}
409
410
411
void SvFileObject::CancelTransfers()
412
1.96k
{
413
    // unsubscribe from the cache if in the middle of loading
414
1.96k
    if( !bDataReady )
415
1.96k
    {
416
        // Do not set-up again
417
1.96k
        bLoadAgain = false;
418
1.96k
        bDataReady = bLoadError = bWaitForData = true;
419
1.96k
        SendStateChg_Impl( sfx2::LinkManager::STATE_LOAD_ABORT );
420
1.96k
    }
421
1.96k
}
422
423
424
void SvFileObject::SendStateChg_Impl( sfx2::LinkManager::LinkState nState )
425
1.96k
{
426
1.96k
    if( !bStateChangeCalled && HasDataLinks() )
427
1.96k
    {
428
1.96k
        DataChanged( SotExchange::GetFormatName(
429
1.96k
                        sfx2::LinkManager::RegisterStatusInfoId()), css::uno::Any(OUString::number( nState )) );
430
1.96k
        bStateChangeCalled = true;
431
1.96k
    }
432
1.96k
}
433
434
435
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */