cid#1640468 Dereference after null check
[LibreOffice.git] / extensions / source / abpilot / datasourcehandling.cxx
blobf48124eda8d15a9842417a8a6b3997459a4b95fb
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 = std::move(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 u"URL"_ustr,
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 for (auto& rDSName : m_pImpl->xContext->getElementNames())
203 m_pImpl->aDataSourceNames.insert(rDSName);
205 catch( const Exception& )
207 TOOLS_WARN_EXCEPTION( "extensions.abpilot", "ODataSourceContext::ODataSourceContext" );
210 ODataSourceContext::~ODataSourceContext()
215 void ODataSourceContext::disambiguate(OUString& _rDataSourceName)
217 OUString sCheck( _rDataSourceName );
218 StringBag::const_iterator aPos = m_pImpl->aDataSourceNames.find( sCheck );
220 sal_Int32 nPostfix = 1;
221 while ( ( m_pImpl->aDataSourceNames.end() != aPos ) && ( nPostfix < 65535 ) )
222 { // there already is a data source with this name
223 sCheck = _rDataSourceName + OUString::number( nPostfix++ );
225 aPos = m_pImpl->aDataSourceNames.find( sCheck );
228 _rDataSourceName = sCheck;
232 void ODataSourceContext::getDataSourceNames( StringBag& _rNames ) const
234 _rNames = m_pImpl->aDataSourceNames;
237 ODataSource ODataSourceContext::createNewThunderbird( const OUString& _rName )
239 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:thunderbird" );
243 ODataSource ODataSourceContext::createNewEvolutionLdap( const OUString& _rName)
245 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:ldap" );
248 ODataSource ODataSourceContext::createNewEvolutionGroupwise( const OUString& _rName)
250 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:groupwise" );
253 ODataSource ODataSourceContext::createNewEvolution( const OUString& _rName)
255 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:evolution:local" );
259 ODataSource ODataSourceContext::createNewKab( const OUString& _rName)
261 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:kab" );
265 ODataSource ODataSourceContext::createNewMacab( const OUString& _rName)
267 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:address:macab" );
271 // tdf117101: Spreadsheet by default
272 ODataSource ODataSourceContext::createNewOther( const OUString& _rName)
274 return lcl_implCreateAndSetURL( m_pImpl->xORB, _rName, "sdbc:calc:" );
277 struct ODataSourceImpl
279 public:
280 Reference< XComponentContext > xORB; /// the service factory
281 Reference< XPropertySet > xDataSource; /// the UNO data source
282 ::utl::SharedUNOComponent< XConnection >
283 xConnection;
284 StringBag aTables; // the cached table names
285 OUString sName;
287 explicit ODataSourceImpl(const Reference< XComponentContext >& _rxORB)
288 : xORB(_rxORB)
294 ODataSource::ODataSource( const ODataSource& _rSource )
296 *this = _rSource;
299 ODataSource& ODataSource::operator=( const ODataSource& _rSource )
301 if( this != &_rSource )
303 m_pImpl.reset( new ODataSourceImpl( *_rSource.m_pImpl ) );
305 return *this;
308 ODataSource& ODataSource::operator=(ODataSource&& _rSource) noexcept
310 m_pImpl = std::move(_rSource.m_pImpl);
311 return *this;
314 ODataSource::ODataSource( const Reference< XComponentContext >& _rxORB )
315 :m_pImpl(new ODataSourceImpl(_rxORB))
319 ODataSource::~ODataSource( )
323 void ODataSource::store(const AddressSettings& rSettings)
325 if (!isValid())
326 // nothing to do
327 return;
330 Reference< XDocumentDataSource > xDocAccess( m_pImpl->xDataSource, UNO_QUERY );
331 Reference< XStorable > xStorable;
332 if ( xDocAccess.is() )
333 xStorable.set(xDocAccess->getDatabaseDocument(), css::uno::UNO_QUERY);
334 OSL_ENSURE( xStorable.is(),"DataSource is no XStorable!" );
335 if ( xStorable.is() )
337 SfxViewFrame* pFrame = SfxViewFrame::Current();
338 SfxObjectShell* pObjectShell = pFrame ? pFrame->GetObjectShell() : nullptr;
339 OUString aOwnURL = lcl_getOwnURL(pObjectShell); // empty if pObjectShell is nullptr
340 if (aOwnURL.isEmpty() || !rSettings.bEmbedDataSource)
342 // Cannot or should not embed.
343 xStorable->storeAsURL(m_pImpl->sName,Sequence<PropertyValue>());
345 else
347 // Embed.
348 OUString aStreamRelPath = u"EmbeddedDatabase"_ustr;
349 const auto& xContext(comphelper::getProcessComponentContext());
350 auto xUri = css::uri::UriReferenceFactory::create(xContext)->parse(aOwnURL);
351 assert(xUri.is());
352 xUri = css::uri::VndSunStarPkgUrlReferenceFactory::create(xContext)->createVndSunStarPkgUrlReference(xUri);
353 assert(xUri.is());
354 OUString const sTmpName = xUri->getUriReference() + "/" + aStreamRelPath;
355 assert(pObjectShell);
356 uno::Reference<embed::XStorage> xStorage = pObjectShell->GetStorage();
357 uno::Sequence<beans::PropertyValue> aSequence = comphelper::InitPropertySequence(
359 {"TargetStorage", uno::Any(xStorage)},
360 {"StreamRelPath", uno::Any(aStreamRelPath)},
361 {"BaseURI", uno::Any(aOwnURL)}
363 xStorable->storeAsURL(sTmpName, aSequence);
364 m_pImpl->sName = sTmpName;
366 // Refer to the sub-storage name in the document settings, so
367 // we can load it again next time the file is imported.
368 uno::Reference<lang::XMultiServiceFactory> xFactory(pObjectShell->GetModel(), uno::UNO_QUERY);
369 uno::Reference<beans::XPropertySet> xPropertySet(xFactory->createInstance(u"com.sun.star.document.Settings"_ustr), uno::UNO_QUERY);
370 xPropertySet->setPropertyValue(u"EmbeddedDatabaseName"_ustr, uno::Any(aStreamRelPath));
374 catch(const Exception&)
376 TOOLS_WARN_EXCEPTION("extensions.abpilot",
377 "caught an exception while creating the data source!");
381 void ODataSource::registerDataSource( const OUString& _sRegisteredDataSourceName)
383 if (!isValid())
384 // nothing to do
385 return;
389 // invalidate ourself
390 lcl_registerDataSource(m_pImpl->xORB,_sRegisteredDataSourceName,m_pImpl->sName);
392 catch(const Exception&)
394 TOOLS_WARN_EXCEPTION("extensions.abpilot",
395 "caught an exception while creating the data source!");
400 void ODataSource::setDataSource( const Reference< XPropertySet >& _rxDS,const OUString& _sName )
402 if (m_pImpl->xDataSource.get() == _rxDS.get())
403 // nothing to do
404 return;
406 if ( isConnected() )
407 disconnect();
409 m_pImpl->sName = _sName;
410 m_pImpl->xDataSource = _rxDS;
414 void ODataSource::remove()
416 if (!isValid())
417 // nothing to do
418 return;
422 // invalidate ourself
423 m_pImpl->xDataSource.clear();
425 catch(const Exception&)
427 TOOLS_WARN_EXCEPTION("extensions.abpilot",
428 "caught an exception while creating the data source!");
433 bool ODataSource::rename( const OUString& _rName )
435 if (!isValid())
436 // nothing to do
437 return false;
439 m_pImpl->sName = _rName;
440 return true;
444 const OUString & ODataSource::getName() const
446 if ( !isValid() )
447 return EMPTY_OUSTRING;
448 return m_pImpl->sName;
452 bool ODataSource::hasTable( const OUString& _rTableName ) const
454 if ( !isConnected() )
455 return false;
457 const StringBag& aTables( getTableNames() );
458 return aTables.find( _rTableName ) != aTables.end();
462 const StringBag& ODataSource::getTableNames() const
464 m_pImpl->aTables.clear();
465 if ( !isConnected() )
467 OSL_FAIL( "ODataSource::getTableNames: not connected!" );
469 else
473 // get the tables container from the connection
474 Reference< XTablesSupplier > xSuppTables( m_pImpl->xConnection.getTyped(), UNO_QUERY );
475 Reference< XNameAccess > xTables;
476 if ( xSuppTables.is( ) )
477 xTables = xSuppTables->getTables();
478 DBG_ASSERT( xTables.is(), "ODataSource::getTableNames: could not retrieve the tables container!" );
480 // get the names
481 Sequence< OUString > aTableNames;
482 if ( xTables.is( ) )
483 aTableNames = xTables->getElementNames( );
485 // copy the names
486 for (auto& rTableName : aTableNames)
487 m_pImpl->aTables.insert(rTableName);
489 catch(const Exception&)
494 // now the table cache is up-to-date
495 return m_pImpl->aTables;
499 bool ODataSource::connect(weld::Window* _pMessageParent)
501 if ( isConnected( ) )
502 // nothing to do
503 return true;
506 // create the interaction handler (needed for authentication and error handling)
507 Reference< XInteractionHandler > xInteractions;
510 xInteractions = InteractionHandler::createWithParent(m_pImpl->xORB, nullptr);
512 catch(const Exception&)
517 // failure to create the interaction handler is a serious issue ...
518 if (!xInteractions.is())
520 if ( _pMessageParent )
521 ShowServiceNotAvailableError( _pMessageParent, u"com.sun.star.task.InteractionHandler", true );
522 return false;
526 // open the connection
527 Any aError;
528 Reference< XConnection > xConnection;
531 Reference< XCompletedConnection > xComplConn( m_pImpl->xDataSource, UNO_QUERY );
532 DBG_ASSERT( xComplConn.is(), "ODataSource::connect: missing the XCompletedConnection interface on the data source!" );
533 if ( xComplConn.is() )
534 xConnection = xComplConn->connectWithCompletion( xInteractions );
536 catch( const SQLContext& e ) { aError <<= e; }
537 catch( const SQLWarning& e ) { aError <<= e; }
538 catch( const SQLException& e ) { aError <<= e; }
539 catch( const Exception& )
541 TOOLS_WARN_EXCEPTION("extensions.abpilot", "");
545 // handle errors
546 if ( aError.hasValue() && _pMessageParent )
550 SQLException aException;
551 aError >>= aException;
552 if ( aException.Message.isEmpty() )
554 // prepend some context info
555 SQLContext aDetailedError(compmodule::ModuleRes(RID_STR_NOCONNECTION), // message
556 {}, {}, 0,
557 aError, // next exception
558 compmodule::ModuleRes(RID_STR_PLEASECHECKSETTINGS)); // details
559 // handle (aka display) the new context info
560 xInteractions->handle( new OInteractionRequest( Any( aDetailedError ) ) );
562 else
564 // handle (aka display) the original error
565 xInteractions->handle( new OInteractionRequest( Any( aException ) ) );
568 catch( const Exception& )
570 TOOLS_WARN_EXCEPTION("extensions.abpilot",
571 "caught an exception while trying to display the error!");
575 if ( !xConnection.is() )
576 return false;
579 // success
580 m_pImpl->xConnection.reset( xConnection );
581 m_pImpl->aTables.clear();
583 return true;
587 void ODataSource::disconnect( )
589 m_pImpl->xConnection.clear();
590 m_pImpl->aTables.clear();
594 bool ODataSource::isConnected( ) const
596 return m_pImpl->xConnection.is();
600 bool ODataSource::isValid() const
602 return m_pImpl && m_pImpl->xDataSource.is();
605 Reference< XPropertySet > ODataSource::getDataSource() const
607 return m_pImpl ? m_pImpl->xDataSource : Reference< XPropertySet >();
611 } // namespace abp
614 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */