Coverage Report

Created: 2025-07-07 10:01

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