LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / appl / linkmgr2.cxx
blob4b77bcf90fb6160c2ac1e6f276eb7b048d8a279d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <comphelper/string.hxx>
21 #include <sfx2/linkmgr.hxx>
22 #include <sfx2/sfxsids.hrc>
23 #include <com/sun/star/document/UpdateDocMode.hpp>
24 #include <osl/file.hxx>
25 #include <sfx2/objsh.hxx>
26 #include <svl/urihelper.hxx>
27 #include <sot/formats.hxx>
28 #include <tools/urlobj.hxx>
29 #include <sot/exchange.hxx>
30 #include <tools/debug.hxx>
31 #include <vcl/filter/SvmReader.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/weld.hxx>
34 #include <vcl/gdimtf.hxx>
35 #include <sfx2/lnkbase.hxx>
36 #include <sfx2/app.hxx>
37 #include <vcl/graph.hxx>
38 #include <svl/stritem.hxx>
39 #include <svl/eitem.hxx>
40 #include <svl/intitem.hxx>
41 #include <i18nlangtag/languagetag.hxx>
42 #include <vcl/dibtools.hxx>
43 #include <unotools/charclass.hxx>
44 #include <unotools/securityoptions.hxx>
45 #include <vcl/GraphicLoader.hxx>
46 #include <vcl/TypeSerializer.hxx>
48 #include "fileobj.hxx"
49 #include "impldde.hxx"
50 #include <sfx2/strings.hrc>
51 #include <sfx2/sfxresid.hxx>
53 #include <com/sun/star/lang/XComponent.hpp>
54 #include <com/sun/star/util/XCloseable.hpp>
56 using ::com::sun::star::uno::UNO_QUERY;
57 using ::com::sun::star::uno::Reference;
58 using ::com::sun::star::lang::XComponent;
59 using ::com::sun::star::util::XCloseable;
61 namespace sfx2
64 namespace {
66 class SvxInternalLink : public sfx2::SvLinkSource
68 public:
69 SvxInternalLink() {}
71 virtual bool Connect( sfx2::SvBaseLink* ) override;
76 LinkManager::LinkManager(SfxObjectShell* p)
77 : pPersist( p )
81 LinkManager::~LinkManager()
83 for(tools::SvRef<SvBaseLink> & rTmp : aLinkTbl)
85 if( rTmp.is() )
87 rTmp->Disconnect();
88 rTmp->SetLinkManager( nullptr );
93 void LinkManager::InsertCachedComp(const Reference<XComponent>& xComp)
95 maCachedComps.push_back(xComp);
98 void LinkManager::CloseCachedComps()
100 for (const auto& rxCachedComp : maCachedComps)
102 Reference<XCloseable> xCloseable(rxCachedComp, UNO_QUERY);
103 if (!xCloseable.is())
104 continue;
106 xCloseable->close(true);
108 maCachedComps.clear();
111 void LinkManager::Remove( SvBaseLink const *pLink )
113 // No duplicate links inserted
114 bool bFound = false;
115 for( size_t n = 0; n < aLinkTbl.size(); )
117 tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
118 if( pLink == rTmp.get() )
120 rTmp->Disconnect();
121 rTmp->SetLinkManager( nullptr );
122 rTmp.clear();
123 bFound = true;
126 // Remove empty ones if they exist
127 if( !rTmp.is() )
129 aLinkTbl.erase( aLinkTbl.begin() + n );
130 if( bFound )
131 return ;
133 else
134 ++n;
138 void LinkManager::Remove( size_t nPos, size_t nCnt )
140 if( !nCnt || nPos >= aLinkTbl.size() )
141 return;
143 if (sal::static_int_cast<size_t>(nPos + nCnt) > aLinkTbl.size())
144 nCnt = aLinkTbl.size() - nPos;
146 for( size_t n = nPos; n < nPos + nCnt; ++n)
148 tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
149 if( rTmp.is() )
151 rTmp->Disconnect();
152 rTmp->SetLinkManager( nullptr );
155 aLinkTbl.erase( aLinkTbl.begin() + nPos, aLinkTbl.begin() + nPos + nCnt );
158 bool LinkManager::Insert( SvBaseLink* pLink )
160 for( size_t n = 0; n < aLinkTbl.size(); ++n )
162 tools::SvRef<SvBaseLink>& rTmp = aLinkTbl[ n ];
163 if( !rTmp.is() )
165 aLinkTbl.erase( aLinkTbl.begin() + n-- );
167 else if( pLink == rTmp.get() )
168 return false; // No duplicate links inserted
171 pLink->SetLinkManager( this );
172 aLinkTbl.emplace_back(pLink );
173 return true;
176 bool LinkManager::InsertLink( SvBaseLink * pLink,
177 SvBaseLinkObjectType nObjType,
178 SfxLinkUpdateMode nUpdateMode,
179 const OUString* pName )
181 // This First
182 pLink->SetObjType( nObjType );
183 if( pName )
184 pLink->SetName( *pName );
185 pLink->SetUpdateMode( nUpdateMode );
186 return Insert( pLink );
189 void LinkManager::InsertDDELink( SvBaseLink * pLink,
190 const OUString& rServer,
191 std::u16string_view rTopic,
192 std::u16string_view rItem )
194 if( !isClientType( pLink->GetObjType() ) )
195 return;
197 OUString sCmd;
198 ::sfx2::MakeLnkName( sCmd, &rServer, rTopic, rItem );
200 pLink->SetObjType( SvBaseLinkObjectType::ClientDde );
201 pLink->SetName( sCmd );
202 Insert( pLink );
205 void LinkManager::InsertDDELink( SvBaseLink * pLink )
207 DBG_ASSERT( isClientType(pLink->GetObjType()), "no OBJECT_CLIENT_SO" );
208 if( !isClientType( pLink->GetObjType() ) )
209 return;
211 if( pLink->GetObjType() == SvBaseLinkObjectType::ClientSo )
212 pLink->SetObjType( SvBaseLinkObjectType::ClientDde );
214 Insert( pLink );
217 // Obtain the string for the dialog
218 bool LinkManager::GetDisplayNames( const SvBaseLink * pLink,
219 OUString* pType,
220 OUString* pFile,
221 OUString* pLinkStr,
222 OUString* pFilter )
224 bool bRet = false;
225 const OUString& sLNm( pLink->GetLinkSourceName() );
226 if( !sLNm.isEmpty() )
228 switch( pLink->GetObjType() )
230 case SvBaseLinkObjectType::ClientFile:
231 case SvBaseLinkObjectType::ClientGraphic:
232 case SvBaseLinkObjectType::ClientOle:
234 sal_Int32 nPos = 0;
235 OUString sFile( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
236 OUString sRange( sLNm.getToken( 0, ::sfx2::cTokenSeparator, nPos ) );
238 if( pFile )
239 *pFile = sFile;
240 if( pLinkStr )
241 *pLinkStr = sRange;
242 if( pFilter )
243 *pFilter = nPos == -1 ? OUString() : sLNm.copy(nPos);
245 if( pType )
247 SvBaseLinkObjectType nObjType = pLink->GetObjType();
248 *pType = SfxResId(
249 ( SvBaseLinkObjectType::ClientFile == nObjType || SvBaseLinkObjectType::ClientOle == nObjType )
250 ? RID_SVXSTR_FILELINK
251 : RID_SVXSTR_GRAPHICLINK);
253 bRet = true;
255 break;
256 case SvBaseLinkObjectType::ClientDde:
258 sal_Int32 nTmp = 0;
259 OUString sServer( sLNm.getToken( 0, cTokenSeparator, nTmp ) );
260 OUString sTopic( sLNm.getToken( 0, cTokenSeparator, nTmp ) );
262 if( pType )
263 *pType = sServer;
264 if( pFile )
265 *pFile = sTopic;
266 if( pLinkStr )
267 *pLinkStr = nTmp != -1 ? sLNm.copy(nTmp) : OUString();
268 bRet = true;
270 break;
271 default:
272 break;
276 return bRet;
279 void LinkManager::UpdateAllLinks(
280 bool bAskUpdate,
281 bool bUpdateGrfLinks,
282 weld::Window* pParentWin )
284 // First make a copy of the array in order to update links
285 // links in ... no contact between them!
286 std::vector<SvBaseLink*> aTmpArr;
287 for( size_t n = 0; n < aLinkTbl.size(); ++n )
289 tools::SvRef<SvBaseLink>& rLink = aLinkTbl[ n ];
290 if( !rLink.is() )
292 Remove( n-- );
293 continue;
295 aTmpArr.push_back( rLink.get() );
298 for(SvBaseLink* pLink : aTmpArr)
300 // search first in the array after the entry
301 bool bFound = false;
302 for(const tools::SvRef<SvBaseLink> & i : aLinkTbl)
303 if( pLink == i.get() )
305 bFound = true;
306 break;
309 if( !bFound )
310 continue; // was not available!
312 // Graphic-Links not to update yet
313 if( !pLink->IsVisible() ||
314 ( !bUpdateGrfLinks && SvBaseLinkObjectType::ClientGraphic == pLink->GetObjType() ))
315 continue;
317 if( bAskUpdate )
319 OUString aMsg = SfxResId(STR_QUERY_UPDATE_LINKS);
320 INetURLObject aURL(pPersist->getDocumentBaseURL());
321 aMsg = aMsg.replaceFirst("%{filename}", aURL.GetLastName());
323 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParentWin,
324 VclMessageType::Question, VclButtonsType::YesNo, aMsg));
325 xQueryBox->set_default_response(RET_YES);
327 int nRet = xQueryBox->run();
328 if( RET_YES != nRet )
330 SfxObjectShell* pShell = pLink->GetLinkManager()->GetPersist();
332 if(pShell)
334 comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = pShell->getEmbeddedObjectContainer();
335 rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false);
338 return ; // nothing should be updated
340 bAskUpdate = false; // once is enough
343 pLink->Update();
345 CloseCachedComps();
348 SvLinkSourceRef LinkManager::CreateObj( SvBaseLink const * pLink )
350 switch( pLink->GetObjType() )
352 case SvBaseLinkObjectType::ClientFile:
353 case SvBaseLinkObjectType::ClientGraphic:
354 case SvBaseLinkObjectType::ClientOle:
355 return new SvFileObject;
356 case SvBaseLinkObjectType::Internal:
357 return new SvxInternalLink;
358 case SvBaseLinkObjectType::ClientDde:
359 return new SvDDEObject;
360 default:
361 return SvLinkSourceRef();
365 bool LinkManager::InsertServer( SvLinkSource* pObj )
367 // no duplicate inserts
368 if( !pObj )
369 return false;
371 return aServerTbl.insert( pObj ).second;
374 void LinkManager::RemoveServer( SvLinkSource* pObj )
376 aServerTbl.erase( pObj );
379 void MakeLnkName( OUString& rName, const OUString* pType, std::u16string_view rFile,
380 std::u16string_view rLink, const OUString* pFilter )
382 if( pType )
384 rName = comphelper::string::strip(*pType, ' ')
385 + OUStringChar(cTokenSeparator);
387 else
388 rName.clear();
390 rName += rFile;
392 rName = comphelper::string::strip(rName, ' ')
393 + OUStringChar(cTokenSeparator);
394 rName = comphelper::string::strip(rName, ' ') + rLink;
395 if( pFilter )
397 rName += OUStringChar(cTokenSeparator) + *pFilter;
398 rName = comphelper::string::strip(rName, ' ');
402 void LinkManager::ReconnectDdeLink(SfxObjectShell& rServer)
404 SfxMedium* pMed = rServer.GetMedium();
405 if (!pMed)
406 return;
408 const ::sfx2::SvBaseLinks& rLinks = GetLinks();
409 size_t n = rLinks.size();
411 for (size_t i = 0; i < n; ++i)
413 ::sfx2::SvBaseLink* p = rLinks[i].get();
414 OUString aType, aFile, aLink, aFilter;
415 if (!GetDisplayNames(p, &aType, &aFile, &aLink, &aFilter))
416 continue;
418 if (aType != "soffice")
419 // DDE connections between OOo apps are always named 'soffice'.
420 continue;
422 OUString aTmp;
423 OUString aURL = aFile;
424 if (osl::FileBase::getFileURLFromSystemPath(aFile, aTmp)
425 == osl::FileBase::E_None)
426 aURL = aTmp;
428 if (!aURL.equalsIgnoreAsciiCase(pMed->GetName()))
429 // This DDE link is not associated with this server shell... Skip it.
430 continue;
432 if (aLink.isEmpty())
433 continue;
435 LinkServerShell(aLink, rServer, *p);
439 void LinkManager::LinkServerShell(const OUString& rPath, SfxObjectShell& rServer, ::sfx2::SvBaseLink& rLink)
441 ::sfx2::SvLinkSource* pSrvSrc = rServer.DdeCreateLinkSource(rPath);
442 if (pSrvSrc)
444 css::datatransfer::DataFlavor aFl;
445 SotExchange::GetFormatDataFlavor(rLink.GetContentType(), aFl);
446 rLink.SetObj(pSrvSrc);
447 pSrvSrc->AddDataAdvise(
448 &rLink, aFl.MimeType,
449 SfxLinkUpdateMode::ONCALL == rLink.GetUpdateMode() ? ADVISEMODE_ONLYONCE : 0);
453 void LinkManager::InsertFileLink(
454 sfx2::SvBaseLink& rLink, SvBaseLinkObjectType nFileType, std::u16string_view rFileNm,
455 const OUString* pFilterNm, const OUString* pRange)
457 if (!isClientType(rLink.GetObjType()))
458 return;
460 OUStringBuffer aBuf(64);
461 aBuf.append(rFileNm);
462 aBuf.append(sfx2::cTokenSeparator);
464 if (pRange)
465 aBuf.append(*pRange);
467 if (pFilterNm)
469 aBuf.append(sfx2::cTokenSeparator);
470 aBuf.append(*pFilterNm);
473 OUString aCmd = aBuf.makeStringAndClear();
474 InsertLink(&rLink, nFileType, SfxLinkUpdateMode::ONCALL, &aCmd);
477 // A transfer is aborted, so cancel all download media
478 // (for now this is only of interest for the file links!)
479 void LinkManager::CancelTransfers()
482 const sfx2::SvBaseLinks& rLnks = GetLinks();
483 for( size_t n = rLnks.size(); n; )
485 const sfx2::SvBaseLink& rLnk = *rLnks[--n];
486 if (isClientFileType(rLnk.GetObjType()))
488 if (SvFileObject* pFileObj = static_cast<SvFileObject*>(rLnk.GetObj()))
489 pFileObj->CancelTransfers();
494 // For the purpose of sending Status information from the file object to
495 // the base link, there exist a dedicated ClipBoardId. The SvData-object
496 // gets the appropriate information as a string
497 // For now this is required for file object in conjunction with JavaScript
498 // - needs information about Load/Abort/Error
499 SotClipboardFormatId LinkManager::RegisterStatusInfoId()
501 static SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
503 if( nFormat == SotClipboardFormatId::NONE )
505 nFormat = SotExchange::RegisterFormatName(
506 "StatusInfo from SvxInternalLink");
508 return nFormat;
511 bool LinkManager::GetGraphicFromAny(const OUString& rMimeType,
512 const css::uno::Any & rValue,
513 Graphic& rGraphic,
514 weld::Window* pParentWin)
516 bool bRet = false;
518 if (!rValue.hasValue())
519 return bRet;
521 if (rValue.has<OUString>())
523 OUString sReferer;
524 SfxObjectShell* sh = GetPersist();
525 if (sh && sh->HasName())
526 sReferer = sh->GetMedium()->GetName();
528 OUString sURL = rValue.get<OUString>();
529 if (!SvtSecurityOptions::isUntrustedReferer(sReferer))
530 rGraphic = vcl::graphic::loadFromURL(sURL, pParentWin);
531 if (rGraphic.IsNone())
532 rGraphic.SetDefaultType();
533 rGraphic.setOriginURL(sURL);
534 return true;
536 else if (rValue.has<css::uno::Sequence<sal_Int8>>())
538 auto aSeq = rValue.get<css::uno::Sequence<sal_Int8>>();
540 SvMemoryStream aMemStm( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(),
541 StreamMode::READ );
542 aMemStm.Seek( 0 );
544 switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
546 case SotClipboardFormatId::SVXB:
548 TypeSerializer aSerializer(aMemStm);
549 aSerializer.readGraphic(rGraphic);
550 bRet = true;
552 break;
553 case SotClipboardFormatId::GDIMETAFILE:
555 GDIMetaFile aMtf;
556 SvmReader aReader( aMemStm );
557 aReader.Read( aMtf );
558 rGraphic = aMtf;
559 bRet = true;
561 break;
562 case SotClipboardFormatId::BITMAP:
564 Bitmap aBmp;
565 ReadDIB(aBmp, aMemStm, true);
566 rGraphic = BitmapEx(aBmp);
567 bRet = true;
569 break;
570 default: break;
573 return bRet;
576 static OUString lcl_DDE_RelToAbs( const OUString& rTopic, const OUString& rBaseURL )
578 OUString sRet;
579 INetURLObject aURL( rTopic );
580 if( INetProtocol::NotValid == aURL.GetProtocol() )
581 osl::FileBase::getFileURLFromSystemPath(rTopic, sRet);
582 if( sRet.isEmpty() )
583 sRet = URIHelper::SmartRel2Abs( INetURLObject(rBaseURL), rTopic, URIHelper::GetMaybeFileHdl() );
584 return sRet;
587 bool SvxInternalLink::Connect( sfx2::SvBaseLink* pLink )
589 SfxObjectShell* pFndShell = nullptr;
590 sal_uInt16 nUpdateMode = css::document::UpdateDocMode::NO_UPDATE;
591 OUString sTopic, sItem, sReferer;
592 LinkManager* pLinkMgr = pLink->GetLinkManager();
593 if (pLinkMgr && sfx2::LinkManager::GetDisplayNames(pLink, nullptr, &sTopic, &sItem) && !sTopic.isEmpty())
595 // first only loop over the DocumentShells the shells and find those
596 // with the name:
597 CharClass aCC( LanguageTag( LANGUAGE_SYSTEM) );
599 bool bFirst = true;
600 SfxObjectShell* pShell = pLinkMgr->GetPersist();
601 if( pShell && pShell->GetMedium() )
603 sReferer = pShell->GetMedium()->GetBaseURL();
604 const SfxUInt16Item* pItem = SfxItemSet::GetItem<SfxUInt16Item>(pShell->GetMedium()->GetItemSet(), SID_UPDATEDOCMODE, false);
605 if ( pItem )
606 nUpdateMode = pItem->GetValue();
609 OUString sNmURL(aCC.lowercase(lcl_DDE_RelToAbs(sTopic, sReferer)));
611 if ( !pShell )
613 bFirst = false;
614 pShell = SfxObjectShell::GetFirst( nullptr, false );
617 OUString sTmp;
618 while( pShell )
620 if( sTmp.isEmpty() )
622 sTmp = pShell->GetTitle( SFX_TITLE_FULLNAME );
623 sTmp = lcl_DDE_RelToAbs(sTmp, sReferer );
627 sTmp = aCC.lowercase( sTmp );
628 if( sTmp == sNmURL ) // we want these
630 pFndShell = pShell;
631 break;
634 if( bFirst )
636 bFirst = false;
637 pShell = SfxObjectShell::GetFirst( nullptr, false );
639 else
640 pShell = SfxObjectShell::GetNext( *pShell, nullptr, false );
642 sTmp.clear();
646 // empty topics are not allowed - which document is it
647 if( sTopic.isEmpty() )
648 return false;
650 if (pFndShell)
652 sfx2::SvLinkSource* pNewSrc = pFndShell->DdeCreateLinkSource( sItem );
653 if( pNewSrc )
655 css::datatransfer::DataFlavor aFl;
656 SotExchange::GetFormatDataFlavor( pLink->GetContentType(), aFl );
658 pLink->SetObj( pNewSrc );
659 pNewSrc->AddDataAdvise( pLink, aFl.MimeType,
660 SfxLinkUpdateMode::ONCALL == pLink->GetUpdateMode()
661 ? ADVISEMODE_ONLYONCE
662 : 0 );
663 return true;
666 else
668 // then try to download the file:
669 INetURLObject aURL( sTopic );
670 INetProtocol eOld = aURL.GetProtocol();
671 sTopic = lcl_DDE_RelToAbs( sTopic, sReferer );
672 aURL.SetURL( sTopic );
673 if( INetProtocol::NotValid != eOld ||
674 INetProtocol::Http != aURL.GetProtocol() )
676 SfxStringItem aName( SID_FILE_NAME, sTopic );
677 SfxBoolItem aMinimized(SID_MINIMIZED, true);
678 SfxBoolItem aHidden(SID_HIDDEN, true);
679 SfxStringItem aTarget( SID_TARGETNAME, "_blank" );
680 SfxStringItem aReferer( SID_REFERER, sReferer );
681 SfxUInt16Item aUpdate( SID_UPDATEDOCMODE, nUpdateMode );
682 SfxBoolItem aReadOnly(SID_DOC_READONLY, false);
684 // Disable automatic re-connection to avoid this link instance
685 // being destroyed at re-connection.
686 SfxBoolItem aDdeConnect(SID_DDE_RECONNECT_ONLOAD, false);
688 // #i14200# (DDE-link crashes wordprocessor)
689 SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
690 aArgs.Put(aReferer);
691 aArgs.Put(aTarget);
692 aArgs.Put(aHidden);
693 aArgs.Put(aMinimized);
694 aArgs.Put(aName);
695 aArgs.Put(aUpdate);
696 aArgs.Put(aReadOnly);
697 aArgs.Put(aDdeConnect);
698 Reference<XComponent> xComp = SfxObjectShell::CreateAndLoadComponent(aArgs);
699 pFndShell = SfxObjectShell::GetShellFromComponent(xComp);
700 if (xComp.is() && pFndShell && pLinkMgr)
702 pLinkMgr->InsertCachedComp(xComp);
703 sfx2::LinkManager::LinkServerShell(sItem, *pFndShell, *pLink);
704 return true;
709 return false;
714 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */