Avoid potential negative array index access to cached text.
[LibreOffice.git] / extensions / source / abpilot / datasourcehandling.cxx
blobfd5820fce1ee22fe958eba81a625d3e88f9f7499
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 .
21 #include <strings.hrc>
22 #include "abptypes.hxx"
23 #include <componentmodule.hxx>
24 #include "datasourcehandling.hxx"
25 #include "addresssettings.hxx"
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/container/XNameAccess.hpp>
29 #include <com/sun/star/frame/XModel.hpp>
30 #include <com/sun/star/frame/XStorable.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/sdb/DatabaseContext.hpp>
33 #include <com/sun/star/sdb/SQLContext.hpp>
34 #include <com/sun/star/sdb/XCompletedConnection.hpp>
35 #include <com/sun/star/sdb/XDatabaseRegistrations.hpp>
36 #include <com/sun/star/sdb/XDocumentDataSource.hpp>
37 #include <com/sun/star/sdbc/XConnection.hpp>
38 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
39 #include <com/sun/star/task/InteractionHandler.hpp>
40 #include <com/sun/star/uri/UriReferenceFactory.hpp>
41 #include <com/sun/star/uri/VndSunStarPkgUrlReferenceFactory.hpp>
43 #include <comphelper/interaction.hxx>
44 #include <comphelper/processfactory.hxx>
45 #include <tools/debug.hxx>
46 #include <comphelper/diagnose_ex.hxx>
47 #include <unotools/sharedunocomponent.hxx>
48 #include <vcl/stdtext.hxx>
49 #include <vcl/weld.hxx>
50 #include <sfx2/objsh.hxx>
51 #include <sfx2/docfile.hxx>
52 #include <sfx2/viewfrm.hxx>
53 #include <comphelper/propertysequence.hxx>
55 namespace
58 /// Returns the URL of this object shell.
59 OUString lcl_getOwnURL(SfxObjectShell const * pObjectShell)
61 OUString aRet;
63 if (!pObjectShell)
64 return aRet;
66 const INetURLObject& rURLObject = pObjectShell->GetMedium()->GetURLObject();
67 aRet = rURLObject.GetMainURL(INetURLObject::DecodeMechanism::NONE);
68 return aRet;
73 namespace abp
77 using namespace ::utl;
78 using namespace ::comphelper;
79 using namespace ::com::sun::star;
80 using namespace ::com::sun::star::uno;
81 using namespace ::com::sun::star::lang;
82 using namespace ::com::sun::star::sdb;
83 using namespace ::com::sun::star::sdbc;
84 using namespace ::com::sun::star::task;
85 using namespace ::com::sun::star::beans;
86 using namespace ::com::sun::star::sdbcx;
87 using namespace ::com::sun::star::container;
88 using namespace ::com::sun::star::frame;
90 static Reference< XDatabaseContext > lcl_getDataSourceContext( const Reference< XComponentContext >& _rxContext )
92 Reference<XDatabaseContext> xContext = DatabaseContext::create(_rxContext);
93 return xContext;
97 /// creates a new data source and inserts it into the context
98 static void lcl_implCreateAndInsert(
99 const Reference< XComponentContext >& _rxContext, const OUString& _rName,
100 Reference< XPropertySet >& /* [out] */ _rxNewDataSource )
103 // get the data source context
104 Reference< XDatabaseContext > xContext = lcl_getDataSourceContext( _rxContext );
106 DBG_ASSERT( !xContext->hasByName( _rName ), "lcl_implCreateAndInsert: name already used!" );
109 // create a new data source
110 Reference< XPropertySet > xNewDataSource;
111 if (xContext.is())
112 xNewDataSource.set( xContext->createInstance(), UNO_QUERY );
113 DBG_ASSERT( xNewDataSource.is(), "lcl_implCreateAndInsert: could not create a new data source!" );
116 // insert the data source into the context
117 DBG_ASSERT( xContext.is(), "lcl_implCreateAndInsert: missing an interface on the context (XNamingService)!" );
118 if (xContext.is())
120 // xDynamicContext->registerObject( _rName, xNewDataSource );
121 _rxNewDataSource = xNewDataSource;
126 /// creates and inserts a data source, and sets its URL property to the string given
127 static ODataSource lcl_implCreateAndSetURL(
128 const Reference< XComponentContext >& _rxORB, const OUString& _rName,
129 const char* _pInitialAsciiURL )
131 ODataSource aReturn( _rxORB );
134 // create the new data source
135 Reference< XPropertySet > xNewDataSource;
136 lcl_implCreateAndInsert( _rxORB, _rName, xNewDataSource );
139 // set the URL property
140 if (xNewDataSource.is())
142 xNewDataSource->setPropertyValue(
143 "URL",
144 Any( OUString::createFromAscii( _pInitialAsciiURL ) )
148 aReturn.setDataSource( xNewDataSource, _rName );
150 catch(const Exception&)
152 TOOLS_WARN_EXCEPTION("extensions.abpilot",
153 "caught an exception while creating the data source!");
156 return aReturn;
159 static void lcl_registerDataSource(
160 const Reference< XComponentContext >& _rxORB, const OUString& _sName,
161 const OUString& _sURL )
163 OSL_ENSURE( !_sName.isEmpty(), "lcl_registerDataSource: invalid name!" );
164 OSL_ENSURE( !_sURL.isEmpty(), "lcl_registerDataSource: invalid URL!" );
167 Reference< XDatabaseContext > xRegistrations( DatabaseContext::create(_rxORB) );
168 if ( xRegistrations->hasRegisteredDatabase( _sName ) )
169 xRegistrations->changeDatabaseLocation( _sName, _sURL );
170 else
171 xRegistrations->registerDatabaseLocation( _sName, _sURL );
173 catch( const Exception& )
175 DBG_UNHANDLED_EXCEPTION("extensions.abpilot");
179 struct ODataSourceContextImpl
181 Reference< XComponentContext > xORB;
182 Reference< XNameAccess > xContext; /// the UNO data source context
183 StringBag aDataSourceNames; /// for quicker name checks (without the UNO overhead)
185 explicit ODataSourceContextImpl(const Reference< XComponentContext >& _rxORB)
186 : xORB(_rxORB)
189 ODataSourceContextImpl(const ODataSourceContextImpl&) = delete;
190 ODataSourceContextImpl& operator=(const ODataSourceContextImpl&) = delete;
193 ODataSourceContext::ODataSourceContext(const Reference< XComponentContext >& _rxORB)
194 :m_pImpl( new ODataSourceContextImpl( _rxORB ) )
198 // create the UNO context
199 m_pImpl->xContext.set( lcl_getDataSourceContext( _rxORB ), UNO_QUERY_THROW );
201 // collect the data source names
202 Sequence< OUString > aDSNames = m_pImpl->xContext->getElementNames();
203 const OUString* pDSNames = aDSNames.getConstArray();
204 const OUString* pDSNamesEnd = pDSNames + aDSNames.getLength();
206 for ( ;pDSNames != pDSNamesEnd; ++pDSNames )
207 m_pImpl->aDataSourceNames.insert( *pDSNames );
209 catch( const Exception& )
211 TOOLS_WARN_EXCEPTION( "extensions.abpilot", "ODataSourceContext::ODataSourceContext" );
214 ODataSourceContext::~ODataSourceContext()
219 void ODataSourceContext::disambiguate(OUString& _rDataSourceName)
221 OUString sCheck( _rDataSourceName );
222 StringBag::const_iterator aPos = m_pImpl->aDataSourceNames.find( sCheck );
224 sal_Int32 nPostfix = 1;
225 while ( ( m_pImpl->aDataSourceNames.end() != aPos ) && ( nPostfix < 65535 ) )
226 { // there already is a data source with this name
227 sCheck = _rDataSourceName + OUString::number( nPostfix++ );
229 aPos = m_pImpl->aDataSourceNames.find( sCheck );
232 _rDataSourceName = sCheck;
236 void ODataSourceContext::getDataSourceNames( StringBag& _rNames ) const
238 _rNames = m_pImpl->aDataSourceNames;
241 ODataSource ODataSourceContext::createNewThunderbird( const OUString& _rName )
243 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:thunderbird" );
247 ODataSource ODataSourceContext::createNewEvolutionLdap( const OUString& _rName)
249 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:ldap" );
252 ODataSource ODataSourceContext::createNewEvolutionGroupwise( const OUString& _rName)
254 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:groupwise" );
257 ODataSource ODataSourceContext::createNewEvolution( const OUString& _rName)
259 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:local" );
263 ODataSource ODataSourceContext::createNewKab( const OUString& _rName)
265 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:kab" );
269 ODataSource ODataSourceContext::createNewMacab( const OUString& _rName)
271 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:macab" );
275 // tdf117101: Spreadsheet by default
276 ODataSource ODataSourceContext::createNewOther( const OUString& _rName)
278 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:calc:" );
281 struct ODataSourceImpl
283 public:
284 Reference< XComponentContext > xORB; /// the service factory
285 Reference< XPropertySet > xDataSource; /// the UNO data source
286 ::utl::SharedUNOComponent< XConnection >
287 xConnection;
288 StringBag aTables; // the cached table names
289 OUString sName;
291 explicit ODataSourceImpl(const Reference< XComponentContext >& _rxORB)
292 : xORB(_rxORB)
298 ODataSource::ODataSource( const ODataSource& _rSource )
300 *this = _rSource;
303 ODataSource& ODataSource::operator=( const ODataSource& _rSource )
305 if( this != &_rSource )
307 m_pImpl.reset( new ODataSourceImpl( *_rSource.m_pImpl ) );
309 return *this;
312 ODataSource& ODataSource::operator=(ODataSource&& _rSource) noexcept
314 m_pImpl = std::move(_rSource.m_pImpl);
315 return *this;
318 ODataSource::ODataSource( const Reference< XComponentContext >& _rxORB )
319 :m_pImpl(new ODataSourceImpl(_rxORB))
323 ODataSource::~ODataSource( )
327 void ODataSource::store(const AddressSettings& rSettings)
329 if (!isValid())
330 // nothing to do
331 return;
334 Reference< XDocumentDataSource > xDocAccess( m_pImpl->xDataSource, UNO_QUERY );
335 Reference< XStorable > xStorable;
336 if ( xDocAccess.is() )
337 xStorable.set(xDocAccess->getDatabaseDocument(), css::uno::UNO_QUERY);
338 OSL_ENSURE( xStorable.is(),"DataSource is no XStorable!" );
339 if ( xStorable.is() )
341 SfxViewFrame* pFrame = SfxViewFrame::Current();
342 SfxObjectShell* pObjectShell = pFrame ? pFrame->GetObjectShell() : nullptr;
343 OUString aOwnURL = lcl_getOwnURL(pObjectShell); // empty if pObjectShell is nullptr
344 if (aOwnURL.isEmpty() || !rSettings.bEmbedDataSource)
346 // Cannot or should not embed.
347 xStorable->storeAsURL(m_pImpl->sName,Sequence<PropertyValue>());
349 else
351 // Embed.
352 OUString aStreamRelPath = "EmbeddedDatabase";
353 auto xContext(comphelper::getProcessComponentContext());
354 auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(aOwnURL);
355 assert(xUri.is());
356 xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext)->createVndSunStarPkgUrlReference(xUri);
357 assert(xUri.is());
358 OUString const sTmpName = xUri->getUriReference() + "/" + aStreamRelPath;
359 assert(pObjectShell);
360 uno::Reference<embed::XStorage> xStorage = pObjectShell->GetStorage();
361 uno::Sequence<beans::PropertyValue> aSequence = comphelper::InitPropertySequence(
363 {"TargetStorage", uno::Any(xStorage)},
364 {"StreamRelPath", uno::Any(aStreamRelPath)},
365 {"BaseURI", uno::Any(aOwnURL)}
367 xStorable->storeAsURL(sTmpName, aSequence);
368 m_pImpl->sName = sTmpName;
370 // Refer to the sub-storage name in the document settings, so
371 // we can load it again next time the file is imported.
372 uno::Reference<lang::XMultiServiceFactory> xFactory(pObjectShell->GetModel(), uno::UNO_QUERY);
373 uno::Reference<beans::XPropertySet> xPropertySet(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);
374 xPropertySet->setPropertyValue("EmbeddedDatabaseName", uno::Any(aStreamRelPath));
378 catch(const Exception&)
380 TOOLS_WARN_EXCEPTION("extensions.abpilot",
381 "caught an exception while creating the data source!");
385 void ODataSource::registerDataSource( const OUString& _sRegisteredDataSourceName)
387 if (!isValid())
388 // nothing to do
389 return;
393 // invalidate ourself
394 lcl_registerDataSource(m_pImpl->xORB,_sRegisteredDataSourceName,m_pImpl->sName);
396 catch(const Exception&)
398 TOOLS_WARN_EXCEPTION("extensions.abpilot",
399 "caught an exception while creating the data source!");
404 void ODataSource::setDataSource( const Reference< XPropertySet >& _rxDS,const OUString& _sName )
406 if (m_pImpl->xDataSource.get() == _rxDS.get())
407 // nothing to do
408 return;
410 if ( isConnected() )
411 disconnect();
413 m_pImpl->sName = _sName;
414 m_pImpl->xDataSource = _rxDS;
418 void ODataSource::remove()
420 if (!isValid())
421 // nothing to do
422 return;
426 // invalidate ourself
427 m_pImpl->xDataSource.clear();
429 catch(const Exception&)
431 TOOLS_WARN_EXCEPTION("extensions.abpilot",
432 "caught an exception while creating the data source!");
437 bool ODataSource::rename( const OUString& _rName )
439 if (!isValid())
440 // nothing to do
441 return false;
443 m_pImpl->sName = _rName;
444 return true;
448 OUString ODataSource::getName() const
450 if ( !isValid() )
451 return OUString();
452 return m_pImpl->sName;
456 bool ODataSource::hasTable( const OUString& _rTableName ) const
458 if ( !isConnected() )
459 return false;
461 const StringBag& aTables( getTableNames() );
462 return aTables.find( _rTableName ) != aTables.end();
466 const StringBag& ODataSource::getTableNames() const
468 m_pImpl->aTables.clear();
469 if ( !isConnected() )
471 OSL_FAIL( "ODataSource::getTableNames: not connected!" );
473 else
477 // get the tables container from the connection
478 Reference< XTablesSupplier > xSuppTables( m_pImpl->xConnection.getTyped(), UNO_QUERY );
479 Reference< XNameAccess > xTables;
480 if ( xSuppTables.is( ) )
481 xTables = xSuppTables->getTables();
482 DBG_ASSERT( xTables.is(), "ODataSource::getTableNames: could not retrieve the tables container!" );
484 // get the names
485 Sequence< OUString > aTableNames;
486 if ( xTables.is( ) )
487 aTableNames = xTables->getElementNames( );
489 // copy the names
490 const OUString* pTableNames = aTableNames.getConstArray();
491 const OUString* pTableNamesEnd = pTableNames + aTableNames.getLength();
492 for (;pTableNames < pTableNamesEnd; ++pTableNames)
493 m_pImpl->aTables.insert( *pTableNames );
495 catch(const Exception&)
500 // now the table cache is up-to-date
501 return m_pImpl->aTables;
505 bool ODataSource::connect(weld::Window* _pMessageParent)
507 if ( isConnected( ) )
508 // nothing to do
509 return true;
512 // create the interaction handler (needed for authentication and error handling)
513 Reference< XInteractionHandler > xInteractions;
516 xInteractions = InteractionHandler::createWithParent(m_pImpl->xORB, nullptr);
518 catch(const Exception&)
523 // failure to create the interaction handler is a serious issue ...
524 if (!xInteractions.is())
526 if ( _pMessageParent )
527 ShowServiceNotAvailableError( _pMessageParent, u"com.sun.star.task.InteractionHandler", true );
528 return false;
532 // open the connection
533 Any aError;
534 Reference< XConnection > xConnection;
537 Reference< XCompletedConnection > xComplConn( m_pImpl->xDataSource, UNO_QUERY );
538 DBG_ASSERT( xComplConn.is(), "ODataSource::connect: missing the XCompletedConnection interface on the data source!" );
539 if ( xComplConn.is() )
540 xConnection = xComplConn->connectWithCompletion( xInteractions );
542 catch( const SQLContext& e ) { aError <<= e; }
543 catch( const SQLWarning& e ) { aError <<= e; }
544 catch( const SQLException& e ) { aError <<= e; }
545 catch( const Exception& )
547 TOOLS_WARN_EXCEPTION("extensions.abpilot", "");
551 // handle errors
552 if ( aError.hasValue() && _pMessageParent )
556 SQLException aException;
557 aError >>= aException;
558 if ( aException.Message.isEmpty() )
560 // prepend some context info
561 SQLContext aDetailedError(compmodule::ModuleRes(RID_STR_NOCONNECTION), // message
562 {}, {}, 0,
563 aError, // next exception
564 compmodule::ModuleRes(RID_STR_PLEASECHECKSETTINGS)); // details
565 // handle (aka display) the new context info
566 xInteractions->handle( new OInteractionRequest( Any( aDetailedError ) ) );
568 else
570 // handle (aka display) the original error
571 xInteractions->handle( new OInteractionRequest( Any( aException ) ) );
574 catch( const Exception& )
576 TOOLS_WARN_EXCEPTION("extensions.abpilot",
577 "caught an exception while trying to display the error!");
581 if ( !xConnection.is() )
582 return false;
585 // success
586 m_pImpl->xConnection.reset( xConnection );
587 m_pImpl->aTables.clear();
589 return true;
593 void ODataSource::disconnect( )
595 m_pImpl->xConnection.clear();
596 m_pImpl->aTables.clear();
600 bool ODataSource::isConnected( ) const
602 return m_pImpl->xConnection.is();
606 bool ODataSource::isValid() const
608 return m_pImpl && m_pImpl->xDataSource.is();
611 Reference< XPropertySet > ODataSource::getDataSource() const
613 return m_pImpl ? m_pImpl->xDataSource : Reference< XPropertySet >();
617 } // namespace abp
620 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */