1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
23 #include <comphelper/string.hxx>
24 #include <rtl/ustring.hxx>
25 #include <svl/svdde.hxx>
26 #include <osl/thread.h>
27 #include <o3tl/sorted_vector.hxx>
28 #include <o3tl/char16_t2wchar_t.hxx>
29 #include <officecfg/Office/Common.hxx>
46 explicit DdeItemImpData( HCONV nH
) : nHCnv( nH
), nCnt( 1 ) {}
49 HDDEDATA CALLBACK
DdeInternal::SvrCallback(
50 UINT nCode
, UINT nCbType
, HCONV hConv
, HSZ hText1
, HSZ hText2
,
51 HDDEDATA hData
, ULONG_PTR
, ULONG_PTR
)
53 DdeInstData
* pInst
= ImpGetInstData();
58 case XTYP_WILDCONNECT
:
60 std::vector
<HSZPAIR
> aPairs
;
62 WCHAR chTopicBuf
[256];
64 DdeQueryStringW( pInst
->hDdeInstSvr
, hText1
, chTopicBuf
,
65 SAL_N_ELEMENTS(chTopicBuf
), CP_WINUNICODE
);
67 for (auto& pService
: DdeService::GetServices())
69 if (hText2
&& !(*pService
->pName
== hText2
))
72 OUString
sTopics(pService
->Topics().replaceAll("\n", "").replaceAll("\r", ""));
73 if (sTopics
.isEmpty())
76 for (sal_Int32 n
= 0; -1 != n
;)
78 OUString
s(sTopics
.getToken(0, '\t', n
));
79 if (hText1
&& s
!= o3tl::toU(chTopicBuf
))
82 DdeString
aDStr(pInst
->hDdeInstSvr
, s
);
83 if (auto pTopic
= FindTopic(*pService
, aDStr
.getHSZ()))
85 auto& pair
= aPairs
.emplace_back();
86 pair
.hszSvc
= pService
->pName
->getHSZ();
87 pair
.hszTopic
= pTopic
->pName
->getHSZ();
94 aPairs
.emplace_back(); // trailing zero
96 HDDEDATA h
= DdeCreateDataHandle(
98 reinterpret_cast<LPBYTE
>(aPairs
.data()),
99 sizeof(HSZPAIR
) * aPairs
.size(),
100 0, nullptr, nCbType
, 0);
105 if (auto pService
= FindService(hText2
))
106 if (FindTopic(*pService
, hText1
))
107 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
110 case XTYP_CONNECT_CONFIRM
:
111 if (auto pService
= FindService(hText2
))
113 if (auto pTopic
= FindTopic(*pService
, hText1
))
115 auto pC
= new Conversation
;
118 pService
->m_vConv
.emplace_back( pC
);
124 DdeService
* pService
= nullptr;
125 Conversation
* pC
= nullptr;
126 for (auto& rpService
: DdeService::GetServices())
128 for ( size_t i
= 0, n
= rpService
->m_vConv
.size(); i
< n
; ++i
)
130 pC
= rpService
->m_vConv
[ i
].get();
131 if ( pC
->hConv
== hConv
)
132 pService
= rpService
;
137 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
140 if ( nCode
== XTYP_DISCONNECT
)
142 DisconnectTopic(*pC
->pTopic
, hConv
);
143 auto it
= std::find_if(pService
->m_vConv
.begin(), pService
->m_vConv
.end(),
144 [&pC
](const std::unique_ptr
<Conversation
>& rxConv
) { return rxConv
.get() == pC
; });
145 if (it
!= pService
->m_vConv
.end())
146 pService
->m_vConv
.erase( it
);
150 bool bExec
= nCode
== XTYP_EXECUTE
;
151 DdeTopic
* pTopic
= pC
->pTopic
;
153 if (pTopic
&& !bExec
&& pService
->HasCbFormat(nCbType
))
154 pItem
= FindItem( *pTopic
, hText2
);
158 if ( !pItem
&& !bExec
)
159 return static_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
161 pTopic
->aItem
= pItem
->GetName();
163 pTopic
->aItem
.clear();
171 OUString aRes
; // Must be free not until the end!
173 if ( pTopic
->IsSystemTopic() )
175 if ( pTopic
->aItem
== SZDDESYS_ITEM_TOPICS
)
176 aRes
= pService
->Topics();
177 else if ( pTopic
->aItem
== SZDDESYS_ITEM_SYSITEMS
)
178 aRes
= pService
->SysItems();
179 else if ( pTopic
->aItem
== SZDDESYS_ITEM_STATUS
)
180 aRes
= pService
->Status();
181 else if ( pTopic
->aItem
== SZDDESYS_ITEM_FORMATS
)
182 aRes
= pService
->Formats();
183 else if ( pTopic
->aItem
== SZDDESYS_ITEM_HELP
)
188 if ( !aRes
.isEmpty() )
189 pData
= new DdeData( aRes
);
193 else if( DDEGETPUTITEM
== pItem
->nType
)
195 pData
= static_cast<DdeGetPutItem
*>(pItem
)->Get( DdeData::GetInternalFormat( nCbType
) );
199 pData
= pTopic
->Get( DdeData::GetInternalFormat( nCbType
));
204 return DdeCreateDataHandle( pInst
->hDdeInstSvr
,
205 static_cast<LPBYTE
>(const_cast<void *>(pData
->xImp
->pData
)),
208 DdeData::GetExternalFormat(
216 if ( !pTopic
->IsSystemTopic() )
219 d
.xImp
->hData
= hData
;
220 d
.xImp
->nFmt
= DdeData::GetInternalFormat( nCbType
);
222 if( DDEGETPUTITEM
== pItem
->nType
)
223 bRes
= static_cast<DdeGetPutItem
*>(pItem
)->Put( &d
);
225 bRes
= pTopic
->Put( &d
);
228 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
230 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
234 // Is the Item turning into a HotLink for the first time?
235 if( !pItem
->pImpData
&& pTopic
->StartAdviseLoop() )
237 // Then the Item has been exchanged
238 std::vector
<DdeItem
*>::iterator
it(std::find(pTopic
->aItems
.begin(),
239 pTopic
->aItems
.end(),
241 if (it
!= pTopic
->aItems
.end())
242 pTopic
->aItems
.erase(it
);
244 std::vector
<DdeItem
*>::iterator iter
;
245 iter
= std::find_if(pTopic
->aItems
.begin(), pTopic
->aItems
.end(),
246 [&hText2
](const DdeItem
* pDdeItem
) { return *pDdeItem
->pName
== hText2
; });
247 if (iter
!= pTopic
->aItems
.end())
249 // It was exchanged indeed
255 // It was not exchange, so back in
256 pTopic
->aItems
.push_back(pItem
);
258 pItem
= iter
!= pTopic
->aItems
.end() ? *iter
: nullptr;
263 IncMonitor(pItem
, hConv
);
266 return reinterpret_cast<HDDEDATA
>(TRUE
);
269 DecMonitor(pItem
, hConv
);
270 return reinterpret_cast<HDDEDATA
>(TRUE
);
275 aExec
.xImp
->hData
= hData
;
276 aExec
.xImp
->nFmt
= DdeData::GetInternalFormat( nCbType
);
280 aName
= static_cast<const sal_Unicode
*>(aExec
.xImp
->pData
);
282 if( pTopic
->IsSystemTopic() )
285 bRes
= pTopic
->Execute( &aName
);
288 return reinterpret_cast<HDDEDATA
>(DDE_FACK
);
290 return reinterpret_cast<HDDEDATA
>(DDE_FNOTPROCESSED
);
296 DdeService
* DdeInternal::FindService( HSZ hService
)
298 DdeServices
& rSvc
= DdeService::GetServices();
299 auto aI
= std::find_if(rSvc
.begin(), rSvc
.end(),
300 [&hService
](const DdeService
* s
) { return *s
->pName
== hService
; });
301 if (aI
!= rSvc
.end())
307 DdeTopic
* DdeInternal::FindTopic( DdeService
& rService
, HSZ hTopic
)
309 std::vector
<DdeTopic
*> &rTopics
= rService
.aTopics
;
311 auto iter
= std::find_if(rTopics
.begin(), rTopics
.end(),
312 [&hTopic
](const DdeTopic
* pTopic
) { return *pTopic
->pName
== hTopic
; });
313 if (iter
!= rTopics
.end())
319 DdeItem
* DdeInternal::FindItem( DdeTopic
& rTopic
, HSZ hItem
)
321 std::vector
<DdeItem
*>::iterator iter
;
322 std::vector
<DdeItem
*> &rItems
= rTopic
.aItems
;
323 DdeInstData
* pInst
= ImpGetInstData();
325 bool bContinue
= false;
328 { // middle check loop
329 iter
= std::find_if(rItems
.begin(), rItems
.end(),
330 [&hItem
](const DdeItem
* pItem
) { return *pItem
->pName
== hItem
; });
331 if (iter
!= rItems
.end())
333 bContinue
= !bContinue
;
337 // Let's query our subclass
339 DdeQueryStringW(pInst
->hDdeInstSvr
,hItem
,chBuf
,SAL_N_ELEMENTS(chBuf
),CP_WINUNICODE
);
340 bContinue
= rTopic
.MakeItem( OUString(o3tl::toU(chBuf
)) );
341 // We need to search again
348 DdeService::DdeService( const OUString
& rService
)
350 DdeInstData
* pInst
= ImpGetInstData();
352 pInst
= ImpInitInstData();
354 pInst
->nInstanceSvr
++;
356 if ( !pInst
->hDdeInstSvr
)
358 nStatus
= DMLERR_SYS_ERROR
;
359 if ( !officecfg::Office::Common::Security::Scripting::DisableActiveContent::get() )
361 nStatus
= sal::static_int_cast
< short >(
362 DdeInitializeW( &pInst
->hDdeInstSvr
,
363 DdeInternal::SvrCallback
,
365 CBF_SKIP_REGISTRATIONS
|
366 CBF_SKIP_UNREGISTRATIONS
, 0 ) );
368 pInst
->pServicesSvr
= new DdeServices
;
371 nStatus
= DMLERR_NO_ERROR
;
373 if ( pInst
->pServicesSvr
)
374 pInst
->pServicesSvr
->push_back( this );
376 pName
= new DdeString( pInst
->hDdeInstSvr
, rService
);
377 if ( nStatus
== DMLERR_NO_ERROR
)
379 if ( !DdeNameService( pInst
->hDdeInstSvr
, pName
->getHSZ(), nullptr,
380 DNS_REGISTER
| DNS_FILTEROFF
) )
382 nStatus
= DMLERR_SYS_ERROR
;
385 AddFormat( SotClipboardFormatId::STRING
);
386 pSysTopic
= new DdeTopic( SZDDESYS_TOPIC
);
387 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_TOPICS
) );
388 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_SYSITEMS
) );
389 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_STATUS
) );
390 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_FORMATS
) );
391 pSysTopic
->AddItem( DdeItem( SZDDESYS_ITEM_HELP
) );
392 AddTopic( *pSysTopic
);
395 DdeService::~DdeService()
397 DdeInstData
* pInst
= ImpGetInstData();
399 if ( pInst
->pServicesSvr
)
400 std::erase(*pInst
->pServicesSvr
, this);
405 pInst
->nInstanceSvr
--;
407 if ( !pInst
->nInstanceSvr
&& pInst
->hDdeInstSvr
)
409 if( DdeUninitialize( pInst
->hDdeInstSvr
) )
411 pInst
->hDdeInstSvr
= 0;
412 delete pInst
->pServicesSvr
;
413 pInst
->pServicesSvr
= nullptr;
414 if( pInst
->nRefCount
== 0)
420 OUString
DdeService::GetName() const
422 return pName
->toOUString();
425 DdeServices
& DdeService::GetServices()
427 DdeInstData
* pInst
= ImpGetInstData();
429 return *(pInst
->pServicesSvr
);
432 void DdeService::AddTopic( const DdeTopic
& rTopic
)
434 RemoveTopic( rTopic
);
435 aTopics
.push_back(const_cast<DdeTopic
*>(&rTopic
));
438 void DdeService::RemoveTopic( const DdeTopic
& rTopic
)
440 auto iter
= std::find_if(aTopics
.begin(), aTopics
.end(),
441 [&rTopic
](const DdeTopic
* pTopic
) { return DdeCmpStringHandles(pTopic
->pName
->getHSZ(), rTopic
.pName
->getHSZ()) == 0; });
442 if (iter
!= aTopics
.end())
445 // Delete all conversions!
446 // Or else we work on deleted topics!
447 for( size_t n
= m_vConv
.size(); n
; )
449 auto const& pC
= m_vConv
[ --n
];
450 if( pC
->pTopic
== &rTopic
)
451 m_vConv
.erase( m_vConv
.begin() + n
);
456 bool DdeService::HasCbFormat( sal_uInt32 nFmt
)
458 return std::find(aFormats
.begin(), aFormats
.end(), nFmt
) != aFormats
.end();
461 bool DdeService::HasFormat(SotClipboardFormatId nFmt
)
463 return HasCbFormat( DdeData::GetExternalFormat( nFmt
));
466 void DdeService::AddFormat(SotClipboardFormatId nFmt
)
468 sal_uInt32 nExternalFmt
= DdeData::GetExternalFormat( nFmt
);
469 if (HasCbFormat(nExternalFmt
))
471 aFormats
.push_back( nExternalFmt
);
474 void DdeService::RemoveFormat(SotClipboardFormatId nFmt
)
476 sal_uInt32 nExternalFmt
= DdeData::GetExternalFormat( nFmt
);
477 auto it
= std::find(aFormats
.begin(), aFormats
.end(), nExternalFmt
);
478 if (it
!= aFormats
.end())
479 aFormats
.erase( it
);
482 DdeTopic::DdeTopic( const OUString
& rName
)
484 DdeInstData
* pInst
= ImpGetInstData();
486 pName
= new DdeString( pInst
->hDdeInstSvr
, rName
);
489 DdeTopic::~DdeTopic()
491 for (auto& rpItem
: aItems
)
493 rpItem
->pMyTopic
= nullptr;
500 OUString
DdeTopic::GetName() const
502 return pName
->toOUString();
505 bool DdeTopic::IsSystemTopic()
507 return GetName() == SZDDESYS_TOPIC
;
510 DdeItem
* DdeTopic::AddItem( const DdeItem
& r
)
513 if( DDEGETPUTITEM
== r
.nType
)
514 s
= new DdeGetPutItem( r
);
516 s
= new DdeItem( r
);
518 aItems
.push_back( s
);
523 void DdeTopic::InsertItem( DdeItem
* pNew
)
527 aItems
.push_back( pNew
);
528 pNew
->pMyTopic
= this;
532 void DdeTopic::RemoveItem( const DdeItem
& r
)
534 auto iter
= std::find_if(aItems
.begin(), aItems
.end(),
535 [&r
](const DdeItem
* pItem
) { return DdeCmpStringHandles(pItem
->pName
->getHSZ(), r
.pName
->getHSZ()) == 0; });
537 if ( iter
!= aItems
.end() )
539 (*iter
)->pMyTopic
= nullptr;
545 void DdeTopic::NotifyClient( const OUString
& rItem
)
547 DdeInstData
* pInst
= ImpGetInstData();
549 auto iter
= std::find_if(aItems
.begin(), aItems
.end(),
550 [&rItem
](const DdeItem
* pItem
) { return pItem
->GetName().equals(rItem
) && pItem
->pImpData
; });
551 if (iter
!= aItems
.end())
552 DdePostAdvise( pInst
->hDdeInstSvr
, pName
->getHSZ(), (*iter
)->pName
->getHSZ() );
555 void DdeInternal::DisconnectTopic(DdeTopic
& rTopic
, HCONV nId
)
557 for (const auto& rpItem
: rTopic
.aItems
)
559 DecMonitor(rpItem
, nId
);
563 DdeData
* DdeTopic::Get(SotClipboardFormatId
/*nFmt*/)
568 bool DdeTopic::Put( const DdeData
* )
573 bool DdeTopic::Execute( const OUString
* )
578 bool DdeTopic::StartAdviseLoop()
583 DdeItem::DdeItem( const sal_Unicode
* p
)
585 DdeInstData
* pInst
= ImpGetInstData();
587 pName
= new DdeString( pInst
->hDdeInstSvr
, OUString(p
) );
593 DdeItem::DdeItem( const OUString
& r
)
595 DdeInstData
* pInst
= ImpGetInstData();
597 pName
= new DdeString( pInst
->hDdeInstSvr
, r
);
603 DdeItem::DdeItem( const DdeItem
& r
)
605 DdeInstData
* pInst
= ImpGetInstData();
607 pName
= new DdeString( pInst
->hDdeInstSvr
, r
.pName
->toOUString() );
616 std::erase(pMyTopic
->aItems
, this);
621 OUString
DdeItem::GetName() const
623 return pName
->toOUString();
626 void DdeItem::NotifyClient()
628 if( pMyTopic
&& pImpData
)
630 DdeInstData
* pInst
= ImpGetInstData();
632 DdePostAdvise( pInst
->hDdeInstSvr
, pMyTopic
->pName
->getHSZ(), pName
->getHSZ() );
636 void DdeInternal::IncMonitor(DdeItem
*const pItem
, HCONV nHCnv
)
638 if (!pItem
->pImpData
)
640 pItem
->pImpData
= new std::vector
<DdeItemImpData
>;
641 if (DDEGETPUTITEM
== pItem
->nType
)
643 static_cast<DdeGetPutItem
*>(pItem
)->AdviseLoop( true );
648 for (size_t n
= pItem
->pImpData
->size(); n
; )
650 if ((*pItem
->pImpData
)[ --n
].nHCnv
== nHCnv
)
652 ++(*pItem
->pImpData
)[ n
].nHCnv
;
658 pItem
->pImpData
->push_back( DdeItemImpData( nHCnv
) );
661 void DdeInternal::DecMonitor(DdeItem
*const pItem
, HCONV nHCnv
)
665 for( size_t n
= 0; n
< pItem
->pImpData
->size(); ++n
)
667 DdeItemImpData
* pData
= &(*pItem
->pImpData
)[n
];
668 if( pData
->nHCnv
== nHCnv
)
670 if( !pData
->nCnt
|| !--pData
->nCnt
)
672 if (1 < pItem
->pImpData
->size())
674 pItem
->pImpData
->erase(pItem
->pImpData
->begin() + n
);
678 delete pItem
->pImpData
;
679 pItem
->pImpData
= nullptr;
680 if (DDEGETPUTITEM
== pItem
->nType
)
682 static_cast<DdeGetPutItem
*>(pItem
)->AdviseLoop(false);
692 short DdeItem::GetLinks()
697 for (const auto& rData
: *pImpData
)
705 DdeGetPutItem::DdeGetPutItem( const sal_Unicode
* p
)
708 nType
= DDEGETPUTITEM
;
711 DdeGetPutItem::DdeGetPutItem( const OUString
& rStr
)
714 nType
= DDEGETPUTITEM
;
717 DdeGetPutItem::DdeGetPutItem( const DdeItem
& rItem
)
720 nType
= DDEGETPUTITEM
;
723 DdeData
* DdeGetPutItem::Get(SotClipboardFormatId
)
728 bool DdeGetPutItem::Put( const DdeData
* )
733 void DdeGetPutItem::AdviseLoop( bool )
737 OUString
DdeService::SysItems()
740 for ( const auto& rpTopic
: aTopics
)
742 if ( rpTopic
->GetName() == SZDDESYS_TOPIC
)
745 for ( const auto& rpItem
: rpTopic
->aItems
)
749 s
+= rpItem
->GetName();
759 OUString
DdeService::Topics()
764 for ( const auto& rpTopic
: aTopics
)
768 s
+= rpTopic
->GetName();
776 OUString
DdeService::Formats()
781 for (size_t i
= 0; i
< aFormats
.size(); ++i
, ++n
)
783 sal_uInt32 f
= aFormats
[ i
];
798 GetClipboardFormatNameW( f
, buf
, SAL_N_ELEMENTS(buf
) );
810 OUString
DdeService::Status()
815 bool DdeTopic::MakeItem( const OUString
& )
820 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */