Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/appl/lnkbase2.cxx
Line
Count
Source
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
21
#include <memory>
22
#include <sfx2/lnkbase.hxx>
23
#include <sot/exchange.hxx>
24
#include <com/sun/star/uno/Any.hxx>
25
#include <com/sun/star/uno/Sequence.hxx>
26
#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
27
#include <comphelper/documentconstants.hxx>
28
#include <sfx2/linkmgr.hxx>
29
#include <vcl/svapp.hxx>
30
#include <vcl/vclenum.hxx>
31
#include <vcl/weld/MessageDialog.hxx>
32
#include <sfx2/strings.hrc>
33
#include <sfx2/sfxresid.hxx>
34
#include <sfx2/filedlghelper.hxx>
35
#include <tools/debug.hxx>
36
#include <svl/svdde.hxx>
37
#include <osl/diagnose.h>
38
#include <officecfg/Office/Common.hxx>
39
40
using namespace ::com::sun::star;
41
using namespace ::com::sun::star::uno;
42
43
namespace sfx2
44
{
45
46
namespace {
47
class ImplDdeItem;
48
}
49
50
// only for internal management
51
struct ImplBaseLinkData
52
{
53
    struct tClientType
54
    {
55
        // applies for all links
56
        SotClipboardFormatId nCntntType; // Update Format
57
        // Not Ole-Links
58
        bool                 bIntrnlLnk;  // It is an internal link
59
        SfxLinkUpdateMode    nUpdateMode; // UpdateMode
60
    };
61
62
    struct tDDEType
63
    {
64
        ImplDdeItem* pItem;
65
    };
66
67
    union {
68
        tClientType ClientType;
69
        tDDEType DDEType;
70
    };
71
    ImplBaseLinkData()
72
112k
    {
73
112k
        ClientType.nCntntType = SotClipboardFormatId::NONE;
74
112k
        ClientType.bIntrnlLnk = false;
75
112k
        ClientType.nUpdateMode = SfxLinkUpdateMode::NONE;
76
112k
        DDEType.pItem = nullptr;
77
112k
    }
78
};
79
80
namespace {
81
82
class ImplDdeItem : public DdeGetPutItem
83
{
84
    SvBaseLink* pLink;
85
    DdeData aData;
86
    Sequence< sal_Int8 > aSeq;  // Datacontainer for DdeData !!!
87
    bool bIsValidData : 1;
88
    bool bIsInDTOR : 1;
89
public:
90
#if defined(_WIN32)
91
    ImplDdeItem( SvBaseLink& rLink, const OUString& rStr )
92
        : DdeGetPutItem( rStr ), pLink( &rLink ), bIsValidData( false ),
93
        bIsInDTOR( false )
94
    {}
95
#endif
96
    virtual ~ImplDdeItem() override;
97
98
    virtual DdeData* Get( SotClipboardFormatId ) override;
99
    virtual bool     Put( const DdeData* ) override;
100
    virtual void     AdviseLoop( bool ) override;
101
102
    void Notify()
103
0
    {
104
0
        bIsValidData = false;
105
0
        DdeGetPutItem::NotifyClient();
106
0
    }
107
108
0
    bool IsInDTOR() const { return bIsInDTOR; }
109
};
110
111
}
112
113
SvBaseLink::SvBaseLink()
114
0
    : m_pLinkMgr( nullptr )
115
0
    , m_pParentWin( nullptr )
116
0
    , m_bIsConnect( false )
117
0
    , m_bIsReadOnly(false)
118
0
{
119
0
    mnObjType = SvBaseLinkObjectType::ClientSo;
120
0
    pImplData.reset( new ImplBaseLinkData );
121
0
    bVisible = bSynchron = true;
122
0
    bWasLastEditOK = false;
123
0
}
124
125
126
SvBaseLink::SvBaseLink( SfxLinkUpdateMode nUpdateMode, SotClipboardFormatId nContentType )
127
112k
    : m_pLinkMgr( nullptr )
128
112k
    , m_pParentWin( nullptr )
129
112k
    , m_bIsConnect( false )
130
112k
    , m_bIsReadOnly(false)
131
112k
{
132
112k
    mnObjType = SvBaseLinkObjectType::ClientSo;
133
112k
    pImplData.reset( new ImplBaseLinkData );
134
112k
    bVisible = bSynchron = true;
135
112k
    bWasLastEditOK = false;
136
137
    // It is going to be an OLE-Link,
138
112k
    pImplData->ClientType.nUpdateMode = nUpdateMode;
139
112k
    pImplData->ClientType.nCntntType = nContentType;
140
112k
    pImplData->ClientType.bIntrnlLnk = false;
141
112k
}
142
143
#if defined(_WIN32)
144
145
static DdeTopic* FindTopic( const OUString & rLinkName, sal_uInt16* pItemStt )
146
{
147
    if( rLinkName.isEmpty() )
148
        return nullptr;
149
150
    OUString sNm( rLinkName );
151
    sal_Int32 nTokenPos = 0;
152
    OUString sService( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
153
154
    DdeServices& rSvc = DdeService::GetServices();
155
    for (auto const& elem : rSvc)
156
    {
157
        if(elem->GetName() == sService)
158
        {
159
            // then we search for the Topic
160
            OUString sTopic( sNm.getToken( 0, cTokenSeparator, nTokenPos ) );
161
            if( pItemStt )
162
                *pItemStt = nTokenPos;
163
164
            std::vector<DdeTopic*>& rTopics = elem->GetTopics();
165
166
            auto cfName = [&sTopic](const DdeTopic* p) { return p->GetName() == sTopic; };
167
            auto it = std::find_if(rTopics.begin(), rTopics.end(), cfName);
168
            if (it == rTopics.end())
169
            {
170
                // Topic not found?
171
                // then we try once to create it
172
                if (elem->MakeTopic(sTopic))
173
                    it = std::find_if(rTopics.begin(), rTopics.end(), cfName);
174
            }
175
            if (it != rTopics.end())
176
                return *it;
177
            break;
178
        }
179
    }
180
    return nullptr;
181
}
182
183
SvBaseLink::SvBaseLink( const OUString& rLinkName, SvBaseLinkObjectType nObjectType, SvLinkSource* pObj )
184
    : m_pLinkMgr( nullptr )
185
    , m_pParentWin( nullptr )
186
    , m_bIsConnect( false )
187
    , m_bIsReadOnly(false)
188
{
189
    bVisible = bSynchron = true;
190
    bWasLastEditOK = false;
191
    aLinkName = rLinkName;
192
    pImplData.reset( new ImplBaseLinkData );
193
    mnObjType = nObjectType;
194
195
    if( !pObj )
196
    {
197
        DBG_ASSERT( pObj, "Where is my left-most object" );
198
        return;
199
    }
200
201
    if( SvBaseLinkObjectType::DdeExternal == mnObjType )
202
    {
203
        sal_uInt16 nItemStt = 0;
204
        DdeTopic* pTopic = FindTopic( aLinkName, &nItemStt );
205
        if( pTopic )
206
        {
207
            // then we have it all together
208
            // MM_TODO how do I get the name
209
            OUString aStr = aLinkName; // xLinkName->GetDisplayName();
210
            aStr = aStr.copy( nItemStt );
211
            pImplData->DDEType.pItem = new ImplDdeItem( *this, aStr );
212
            pTopic->InsertItem( pImplData->DDEType.pItem );
213
214
            // store the Advice
215
            xObj = pObj;
216
        }
217
    }
218
    else if( pObj->Connect( this ) )
219
        xObj = pObj;
220
}
221
222
#endif
223
224
SvBaseLink::~SvBaseLink()
225
112k
{
226
112k
    Disconnect();
227
228
112k
    if( mnObjType == SvBaseLinkObjectType::DdeExternal )
229
0
    {
230
0
        if( !pImplData->DDEType.pItem->IsInDTOR() )
231
0
            delete pImplData->DDEType.pItem;
232
0
    }
233
234
112k
    pImplData.reset();
235
112k
}
236
237
IMPL_LINK( SvBaseLink, EndEditHdl, const OUString&, _rNewName, void )
238
0
{
239
0
    OUString sNewName = _rNewName;
240
0
    if ( !ExecuteEdit( sNewName ) )
241
0
        sNewName.clear();
242
0
    bWasLastEditOK = !sNewName.isEmpty();
243
0
    m_aEndEditLink.Call( *this );
244
0
}
245
246
247
void SvBaseLink::SetObjType( SvBaseLinkObjectType mnObjTypeP )
248
112k
{
249
112k
    DBG_ASSERT( mnObjType != SvBaseLinkObjectType::ClientDde, "type already set" );
250
112k
    DBG_ASSERT( !xObj.is(), "object exist" );
251
252
112k
    mnObjType = mnObjTypeP;
253
112k
}
254
255
256
void SvBaseLink::SetName( const OUString & rNm )
257
112k
{
258
112k
    aLinkName = rNm;
259
112k
}
260
261
262
void SvBaseLink::SetObj( SvLinkSource * pObj )
263
0
{
264
0
    DBG_ASSERT( (isClientType(mnObjType) &&
265
0
                 pImplData->ClientType.bIntrnlLnk) ||
266
0
                mnObjType == SvBaseLinkObjectType::ClientGraphic,
267
0
                "no intern link" );
268
0
    xObj = pObj;
269
0
}
270
271
272
void SvBaseLink::SetLinkSourceName( const OUString & rLnkNm )
273
0
{
274
0
    if( aLinkName == rLnkNm )
275
0
        return;
276
277
0
    AddNextRef(); // should be superfluous
278
    // remove old connection
279
0
    Disconnect();
280
281
0
    aLinkName = rLnkNm;
282
283
    // New Connection
284
0
    GetRealObject_();
285
0
    ReleaseRef(); // should be superfluous
286
0
}
287
288
289
void SvBaseLink::SetUpdateMode( SfxLinkUpdateMode nMode )
290
114k
{
291
114k
    if( isClientType(mnObjType) &&
292
114k
        pImplData->ClientType.nUpdateMode != nMode )
293
2.01k
    {
294
2.01k
        AddNextRef();
295
2.01k
        Disconnect();
296
297
2.01k
        pImplData->ClientType.nUpdateMode = nMode;
298
2.01k
        GetRealObject_();
299
2.01k
        ReleaseRef();
300
2.01k
    }
301
114k
}
302
303
// #i88291#
304
void SvBaseLink::clearStreamToLoadFrom()
305
0
{
306
0
    m_xInputStreamToLoadFrom.clear();
307
0
    if( xObj.is() )
308
0
    {
309
0
        xObj->clearStreamToLoadFrom();
310
0
    }
311
0
}
312
313
bool SvBaseLink::Update()
314
8.37k
{
315
8.37k
    if(officecfg::Office::Common::Security::Scripting::DisableActiveContent::get())
316
0
        return false;
317
318
8.37k
    if( isClientType(mnObjType) )
319
8.37k
    {
320
8.37k
        AddNextRef();
321
8.37k
        Disconnect();
322
323
8.37k
        GetRealObject_();
324
8.37k
        ReleaseRef();
325
8.37k
        if( xObj.is() )
326
8.37k
        {
327
8.37k
            xObj->setStreamToLoadFrom(m_xInputStreamToLoadFrom,m_bIsReadOnly);
328
8.37k
            OUString sMimeType( SotExchange::GetFormatMimeType(
329
8.37k
                            pImplData->ClientType.nCntntType ));
330
8.37k
            Any aData;
331
332
8.37k
            if( xObj->GetData( aData, sMimeType ) )
333
8.37k
            {
334
8.37k
                UpdateResult eRes = DataChanged(sMimeType, aData);
335
8.37k
                bool bSuccess = eRes == SUCCESS;
336
                //for manual Updates there is no need to hold the ServerObject
337
8.37k
                if( SvBaseLinkObjectType::ClientDde == mnObjType &&
338
0
                    SfxLinkUpdateMode::ONCALL == GetUpdateMode() && xObj.is() )
339
0
                    xObj->RemoveAllDataAdvise( this );
340
8.37k
                return bSuccess;
341
8.37k
            }
342
0
            if( xObj.is() )
343
0
            {
344
                // should be asynchronous?
345
0
                if( xObj->IsPending() )
346
0
                    return true;
347
348
                // we do not need the object anymore
349
0
                AddNextRef();
350
0
                Disconnect();
351
0
                ReleaseRef();
352
0
            }
353
0
        }
354
8.37k
    }
355
0
    return false;
356
8.37k
}
357
358
359
SfxLinkUpdateMode SvBaseLink::GetUpdateMode() const
360
0
{
361
0
    return isClientType(mnObjType)
362
0
            ? pImplData->ClientType.nUpdateMode
363
0
            : SfxLinkUpdateMode::ONCALL;
364
0
}
365
366
367
void SvBaseLink::GetRealObject_( bool bConnect)
368
92.4k
{
369
92.4k
    if( !m_pLinkMgr )
370
2.01k
        return;
371
372
90.4k
    DBG_ASSERT( !xObj.is(), "object already exist" );
373
374
90.4k
    if( SvBaseLinkObjectType::ClientDde == mnObjType )
375
0
    {
376
0
        OUString sServer;
377
0
        if( sfx2::LinkManager::GetDisplayNames( this, &sServer ) &&
378
0
            sServer == Application::GetAppName() )  // internal Link !!!
379
0
        {
380
            // so that the Internal link can be created!
381
0
            mnObjType = SvBaseLinkObjectType::Internal;
382
0
            xObj = sfx2::LinkManager::CreateObj( this );
383
384
0
            pImplData->ClientType.bIntrnlLnk = true;
385
0
            mnObjType = SvBaseLinkObjectType::ClientDde;  // so we know what it once was!
386
0
        }
387
0
        else
388
0
        {
389
0
            pImplData->ClientType.bIntrnlLnk = false;
390
0
            xObj = sfx2::LinkManager::CreateObj( this );
391
0
        }
392
0
    }
393
90.4k
    else if( isClientType(mnObjType) )
394
90.4k
        xObj = sfx2::LinkManager::CreateObj( this );
395
396
90.4k
    if( bConnect && ( !xObj.is() || !xObj->Connect( this ) ) )
397
0
        Disconnect();
398
90.4k
}
399
400
SotClipboardFormatId SvBaseLink::GetContentType() const
401
109k
{
402
109k
    if( isClientType(mnObjType) )
403
109k
        return pImplData->ClientType.nCntntType;
404
405
0
    return SotClipboardFormatId::NONE;  // all Formats ?
406
109k
}
407
408
409
void SvBaseLink::SetContentType( SotClipboardFormatId nType )
410
105k
{
411
105k
    if( isClientType(mnObjType) )
412
105k
    {
413
105k
        pImplData->ClientType.nCntntType = nType;
414
105k
    }
415
105k
}
416
417
LinkManager* SvBaseLink::GetLinkManager()
418
263k
{
419
263k
    return m_pLinkMgr;
420
263k
}
421
422
const LinkManager* SvBaseLink::GetLinkManager() const
423
0
{
424
0
    return m_pLinkMgr;
425
0
}
426
427
void SvBaseLink::SetLinkManager( LinkManager* _pMgr )
428
225k
{
429
225k
    m_pLinkMgr = _pMgr;
430
225k
}
431
432
void SvBaseLink::Disconnect()
433
339k
{
434
339k
    if( xObj.is() )
435
90.4k
    {
436
90.4k
        xObj->RemoveAllDataAdvise( this );
437
90.4k
        xObj->RemoveConnectAdvise( this );
438
90.4k
        xObj.clear();
439
90.4k
    }
440
339k
}
441
442
SvBaseLink::UpdateResult SvBaseLink::DataChanged( const OUString &, const css::uno::Any & )
443
0
{
444
0
    if ( mnObjType == SvBaseLinkObjectType::DdeExternal )
445
0
    {
446
0
        if( pImplData->DDEType.pItem )
447
0
            pImplData->DDEType.pItem->Notify();
448
0
    }
449
0
    return SUCCESS;
450
0
}
451
452
void SvBaseLink::Edit(weld::Window* pParent, const Link<SvBaseLink&,void>& rEndEditHdl )
453
0
{
454
0
    m_pParentWin = pParent;
455
0
    m_aEndEditLink = rEndEditHdl;
456
0
    m_bIsConnect = xObj.is();
457
0
    if( !m_bIsConnect )
458
0
        GetRealObject_( xObj.is() );
459
460
0
    bool bAsync = false;
461
0
    Link<const OUString&, void> aLink = LINK( this, SvBaseLink, EndEditHdl );
462
463
0
    if( isClientType(mnObjType) && pImplData->ClientType.bIntrnlLnk )
464
0
    {
465
0
        if( m_pLinkMgr )
466
0
        {
467
0
            SvLinkSourceRef ref = sfx2::LinkManager::CreateObj( this );
468
0
            if( ref.is() )
469
0
            {
470
0
                ref->Edit( pParent, this, aLink );
471
0
                bAsync = true;
472
0
            }
473
0
        }
474
0
    }
475
0
    else
476
0
    {
477
0
        xObj->Edit( pParent, this, aLink );
478
0
        bAsync = true;
479
0
    }
480
481
0
    if ( !bAsync )
482
0
    {
483
0
        ExecuteEdit( OUString() );
484
0
        bWasLastEditOK = false;
485
0
        m_aEndEditLink.Call( *this );
486
0
    }
487
0
}
488
489
bool SvBaseLink::ExecuteEdit( const OUString& _rNewName )
490
0
{
491
0
    if( !_rNewName.isEmpty() )
492
0
    {
493
0
        SetLinkSourceName( _rNewName );
494
0
        if( !Update() )
495
0
        {
496
0
            OUString sApp, sTopic, sItem, sError;
497
0
            sfx2::LinkManager::GetDisplayNames( this, &sApp, &sTopic, &sItem );
498
0
            if( mnObjType == SvBaseLinkObjectType::ClientDde )
499
0
            {
500
0
                sError = SfxResId(STR_DDE_ERROR);
501
502
0
                sal_Int32 nFndPos = sError.indexOf( "%1" );
503
0
                if( -1 != nFndPos )
504
0
                {
505
0
                    sError = sError.replaceAt( nFndPos, 2, sApp );
506
0
                    nFndPos = nFndPos + sApp.getLength();
507
508
0
                    if( -1 != ( nFndPos = sError.indexOf( "%2", nFndPos )))
509
0
                    {
510
0
                        sError = sError.replaceAt( nFndPos, 2, sTopic );
511
0
                        nFndPos = nFndPos + sTopic.getLength();
512
513
0
                        if( -1 != ( nFndPos = sError.indexOf( "%3", nFndPos )))
514
0
                            sError = sError.replaceAt( nFndPos, 2, sItem );
515
0
                    }
516
0
                }
517
0
            }
518
0
            else
519
0
                return false;
520
521
0
            std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_pParentWin,
522
0
                                                                     VclMessageType::Warning, VclButtonsType::Ok, sError));
523
0
            xBox->run();
524
0
        }
525
0
    }
526
0
    else if( !m_bIsConnect )
527
0
        Disconnect();
528
0
    m_bIsConnect = false;
529
0
    return true;
530
0
}
531
532
void SvBaseLink::Closed()
533
0
{
534
0
    if( xObj.is() )
535
0
        xObj->RemoveAllDataAdvise( this );
536
0
}
537
538
FileDialogHelper & SvBaseLink::GetInsertFileDialog(const OUString& rFactory)
539
0
{
540
0
    m_pFileDlg.reset( new FileDialogHelper(
541
0
            ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
542
0
            FileDialogFlags::Insert, rFactory, SfxFilterFlags::NONE, SfxFilterFlags::NONE, m_pParentWin) );
543
0
    return *m_pFileDlg;
544
0
}
545
546
ImplDdeItem::~ImplDdeItem()
547
{
548
    bIsInDTOR = true;
549
    // So that no-one gets the idea to delete the pointer when Disconnecting!
550
    tools::SvRef<SvBaseLink> aRef( pLink );
551
    aRef->Disconnect();
552
}
553
554
DdeData* ImplDdeItem::Get( SotClipboardFormatId nFormat )
555
0
{
556
0
    if( pLink->GetObj() )
557
0
    {
558
0
        // is it still valid?
559
0
        if( bIsValidData && nFormat == aData.GetFormat() )
560
0
            return &aData;
561
0
562
0
        Any aValue;
563
0
        OUString sMimeType( SotExchange::GetFormatMimeType( nFormat ));
564
0
        if( pLink->GetObj()->GetData( aValue, sMimeType ) )
565
0
        {
566
0
            if( aValue >>= aSeq )
567
0
            {
568
0
                aData = DdeData( aSeq.getConstArray(), aSeq.getLength(), nFormat );
569
0
570
0
                bIsValidData = true;
571
0
                return &aData;
572
0
            }
573
0
        }
574
0
    }
575
0
    aSeq.realloc( 0 );
576
0
    bIsValidData = false;
577
0
    return nullptr;
578
0
}
579
580
581
bool ImplDdeItem::Put( const DdeData*  )
582
0
{
583
0
    OSL_FAIL( "ImplDdeItem::Put not implemented" );
584
0
    return false;
585
0
}
586
587
588
void ImplDdeItem::AdviseLoop( bool bOpen )
589
0
{
590
0
    // Connection is closed, so also unsubscribe link
591
0
    if( !pLink->GetObj() )
592
0
        return;
593
0
594
0
    if( bOpen )
595
0
    {
596
0
        // A connection is re-established
597
0
        if( SvBaseLinkObjectType::DdeExternal == pLink->GetObjType() )
598
0
        {
599
0
            pLink->GetObj()->AddDataAdvise( pLink, u"text/plain;charset=utf-16"_ustr,  ADVISEMODE_NODATA );
600
0
            pLink->GetObj()->AddConnectAdvise( pLink );
601
0
        }
602
0
    }
603
0
    else
604
0
    {
605
0
        // So that no-one gets the idea to delete the pointer
606
0
        // when Disconnecting!
607
0
        tools::SvRef<SvBaseLink> aRef( pLink );
608
0
        aRef->Disconnect();
609
0
    }
610
0
}
611
612
}
613
614
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */