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 #include "datasource.hxx"
21 #include "commandcontainer.hxx"
22 #include <stringconstants.hxx>
23 #include <core_resource.hxx>
24 #include <strings.hrc>
25 #include "connection.hxx"
26 #include "SharedConnection.hxx"
27 #include "databasedocument.hxx"
28 #include <OAuthenticationContinuation.hxx>
29
30 #include <hsqlimport.hxx>
31 #include <migrwarndlg.hxx>
32
33 #include <com/sun/star/beans/NamedValue.hpp>
34 #include <com/sun/star/beans/PropertyAttribute.hpp>
35 #include <com/sun/star/beans/PropertyState.hpp>
36 #include <com/sun/star/document/XDocumentSubStorageSupplier.hpp>
37 #include <com/sun/star/lang/DisposedException.hpp>
38 #include <com/sun/star/reflection/ProxyFactory.hpp>
39 #include <com/sun/star/sdb/DatabaseContext.hpp>
40 #include <com/sun/star/sdb/SQLContext.hpp>
41 #include <com/sun/star/sdbc/ConnectionPool.hpp>
42 #include <com/sun/star/sdbc/XDriverAccess.hpp>
43 #include <com/sun/star/sdbc/XDriverManager.hpp>
44 #include <com/sun/star/sdbc/DriverManager.hpp>
45 #include <com/sun/star/ucb/AuthenticationRequest.hpp>
46 #include <com/sun/star/ucb/XInteractionSupplyAuthentication.hpp>
47
48 #include <cppuhelper/implbase.hxx>
49 #include <comphelper/interaction.hxx>
50 #include <comphelper/property.hxx>
51 #include <comphelper/sequence.hxx>
52 #include <comphelper/types.hxx>
53 #include <cppuhelper/supportsservice.hxx>
54 #include <connectivity/dbexception.hxx>
55 #include <connectivity/dbtools.hxx>
56 #include <cppuhelper/typeprovider.hxx>
57 #include <officecfg/Office/Common.hxx>
58 #include <tools/diagnose_ex.h>
59 #include <osl/diagnose.h>
60 #include <osl/process.h>
61 #include <sal/log.hxx>
62 #include <tools/urlobj.hxx>
63 #include <unotools/sharedunocomponent.hxx>
64 #include <rtl/digest.h>
65
66 #include <algorithm>
67 #include <iterator>
68 #include <set>
69
70 #include <config_firebird.h>
71
72 using namespace ::com::sun::star::sdbc;
73 using namespace ::com::sun::star::sdbcx;
74 using namespace ::com::sun::star::sdb;
75 using namespace ::com::sun::star::beans;
76 using namespace ::com::sun::star::uno;
77 using namespace ::com::sun::star::lang;
78 using namespace ::com::sun::star::embed;
79 using namespace ::com::sun::star::container;
80 using namespace ::com::sun::star::util;
81 using namespace ::com::sun::star::io;
82 using namespace ::com::sun::star::task;
83 using namespace ::com::sun::star::ucb;
84 using namespace ::com::sun::star::frame;
85 using namespace ::com::sun::star::reflection;
86 using namespace ::cppu;
87 using namespace ::osl;
88 using namespace ::dbtools;
89 using namespace ::comphelper;
90
91 namespace dbaccess
92 {
93
94 namespace {
95
96 /** helper class which implements a XFlushListener, and forwards all
97 notification events to another XFlushListener
98
99 The speciality is that the foreign XFlushListener instance, to which
100 the notifications are forwarded, is held weak.
101
102 Thus, the class can be used with XFlushable instance which hold
103 their listeners with a hard reference, if you simply do not *want*
104 to be held hard-ref-wise.
105 */
106 class FlushNotificationAdapter : public ::cppu::WeakImplHelper< XFlushListener >
107 {
108 private:
109 WeakReference< XFlushable > m_aBroadcaster;
110 WeakReference< XFlushListener > m_aListener;
111
112 public:
installAdapter(const Reference<XFlushable> & _rxBroadcaster,const Reference<XFlushListener> & _rxListener)113 static void installAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener )
114 {
115 new FlushNotificationAdapter( _rxBroadcaster, _rxListener );
116 }
117
118 protected:
119 FlushNotificationAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener );
120 virtual ~FlushNotificationAdapter() override;
121
122 void impl_dispose();
123
124 protected:
125 // XFlushListener
126 virtual void SAL_CALL flushed( const css::lang::EventObject& rEvent ) override;
127 // XEventListener
128 virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
129 };
130
131 }
132
FlushNotificationAdapter(const Reference<XFlushable> & _rxBroadcaster,const Reference<XFlushListener> & _rxListener)133 FlushNotificationAdapter::FlushNotificationAdapter( const Reference< XFlushable >& _rxBroadcaster, const Reference< XFlushListener >& _rxListener )
134 :m_aBroadcaster( _rxBroadcaster )
135 ,m_aListener( _rxListener )
136 {
137 OSL_ENSURE( _rxBroadcaster.is(), "FlushNotificationAdapter::FlushNotificationAdapter: invalid flushable!" );
138
139 osl_atomic_increment( &m_refCount );
140 {
141 if ( _rxBroadcaster.is() )
142 _rxBroadcaster->addFlushListener( this );
143 }
144 osl_atomic_decrement( &m_refCount );
145 OSL_ENSURE( m_refCount == 1, "FlushNotificationAdapter::FlushNotificationAdapter: broadcaster isn't holding by hard ref!?" );
146 }
147
~FlushNotificationAdapter()148 FlushNotificationAdapter::~FlushNotificationAdapter()
149 {
150 }
151
impl_dispose()152 void FlushNotificationAdapter::impl_dispose()
153 {
154 Reference< XFlushListener > xKeepAlive( this );
155
156 Reference< XFlushable > xFlushable( m_aBroadcaster );
157 if ( xFlushable.is() )
158 xFlushable->removeFlushListener( this );
159
160 m_aListener.clear();
161 m_aBroadcaster.clear();
162 }
163
flushed(const EventObject & rEvent)164 void SAL_CALL FlushNotificationAdapter::flushed( const EventObject& rEvent )
165 {
166 Reference< XFlushListener > xListener( m_aListener );
167 if ( xListener.is() )
168 xListener->flushed( rEvent );
169 else
170 impl_dispose();
171 }
172
disposing(const EventObject & Source)173 void SAL_CALL FlushNotificationAdapter::disposing( const EventObject& Source )
174 {
175 Reference< XFlushListener > xListener( m_aListener );
176 if ( xListener.is() )
177 xListener->disposing( Source );
178
179 impl_dispose();
180 }
181
OAuthenticationContinuation()182 OAuthenticationContinuation::OAuthenticationContinuation()
183 :m_bRememberPassword(true), // TODO: a meaningful default
184 m_bCanSetUserName(true)
185 {
186 }
187
canSetRealm()188 sal_Bool SAL_CALL OAuthenticationContinuation::canSetRealm( )
189 {
190 return false;
191 }
192
setRealm(const OUString &)193 void SAL_CALL OAuthenticationContinuation::setRealm( const OUString& /*Realm*/ )
194 {
195 SAL_WARN("dbaccess","OAuthenticationContinuation::setRealm: not supported!");
196 }
197
canSetUserName()198 sal_Bool SAL_CALL OAuthenticationContinuation::canSetUserName( )
199 {
200 // we always allow this, even if the database document is read-only. In this case,
201 // it's simply that the user cannot store the new user name.
202 return m_bCanSetUserName;
203 }
204
setUserName(const OUString & _rUser)205 void SAL_CALL OAuthenticationContinuation::setUserName( const OUString& _rUser )
206 {
207 m_sUser = _rUser;
208 }
209
canSetPassword()210 sal_Bool SAL_CALL OAuthenticationContinuation::canSetPassword( )
211 {
212 return true;
213 }
214
setPassword(const OUString & _rPassword)215 void SAL_CALL OAuthenticationContinuation::setPassword( const OUString& _rPassword )
216 {
217 m_sPassword = _rPassword;
218 }
219
getRememberPasswordModes(RememberAuthentication & _reDefault)220 Sequence< RememberAuthentication > SAL_CALL OAuthenticationContinuation::getRememberPasswordModes( RememberAuthentication& _reDefault )
221 {
222 _reDefault = RememberAuthentication_SESSION;
223 return { _reDefault };
224 }
225
setRememberPassword(RememberAuthentication _eRemember)226 void SAL_CALL OAuthenticationContinuation::setRememberPassword( RememberAuthentication _eRemember )
227 {
228 m_bRememberPassword = (RememberAuthentication_NO != _eRemember);
229 }
230
canSetAccount()231 sal_Bool SAL_CALL OAuthenticationContinuation::canSetAccount( )
232 {
233 return false;
234 }
235
setAccount(const OUString &)236 void SAL_CALL OAuthenticationContinuation::setAccount( const OUString& )
237 {
238 SAL_WARN("dbaccess","OAuthenticationContinuation::setAccount: not supported!");
239 }
240
getRememberAccountModes(RememberAuthentication & _reDefault)241 Sequence< RememberAuthentication > SAL_CALL OAuthenticationContinuation::getRememberAccountModes( RememberAuthentication& _reDefault )
242 {
243 _reDefault = RememberAuthentication_NO;
244 return { RememberAuthentication_NO };
245 }
246
setRememberAccount(RememberAuthentication)247 void SAL_CALL OAuthenticationContinuation::setRememberAccount( RememberAuthentication /*Remember*/ )
248 {
249 SAL_WARN("dbaccess","OAuthenticationContinuation::setRememberAccount: not supported!");
250 }
251
252 namespace {
253
254 /** The class OSharedConnectionManager implements a structure to share connections.
255 It owns the master connections which will be disposed when the last connection proxy is gone.
256 */
257 // need to hold the digest
258 struct TDigestHolder
259 {
260 sal_uInt8 m_pBuffer[RTL_DIGEST_LENGTH_SHA1];
TDigestHolderdbaccess::__anon5ce340130211::TDigestHolder261 TDigestHolder()
262 {
263 m_pBuffer[0] = 0;
264 }
265
266 };
267
268 }
269
270 class OSharedConnectionManager : public ::cppu::WeakImplHelper< XEventListener >
271 {
272
273 // contains the currently used master connections
274 struct TConnectionHolder
275 {
276 Reference< XConnection > xMasterConnection;
277 oslInterlockedCount nALiveCount;
278 };
279
280 // the less-compare functor, used for the stl::map
281 struct TDigestLess
282 {
operator ()dbaccess::OSharedConnectionManager::TDigestLess283 bool operator() (const TDigestHolder& x, const TDigestHolder& y) const
284 {
285 sal_uInt32 i;
286 for(i=0;i < RTL_DIGEST_LENGTH_SHA1 && (x.m_pBuffer[i] >= y.m_pBuffer[i]); ++i)
287 ;
288 return i < RTL_DIGEST_LENGTH_SHA1;
289 }
290 };
291
292 typedef std::map< TDigestHolder,TConnectionHolder,TDigestLess> TConnectionMap; // holds the master connections
293 typedef std::map< Reference< XConnection >,TConnectionMap::iterator> TSharedConnectionMap;// holds the shared connections
294
295 ::osl::Mutex m_aMutex;
296 TConnectionMap m_aConnections; // remember the master connection in conjunction with the digest
297 TSharedConnectionMap m_aSharedConnection; // the shared connections with conjunction with an iterator into the connections map
298 Reference< XProxyFactory > m_xProxyFactory;
299
300 protected:
301 virtual ~OSharedConnectionManager() override;
302
303 public:
304 explicit OSharedConnectionManager(const Reference< XComponentContext >& _rxContext);
305
306 void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
307 Reference<XConnection> getConnection( const OUString& url,
308 const OUString& user,
309 const OUString& password,
310 const Sequence< PropertyValue >& _aInfo,
311 ODatabaseSource* _pDataSource);
312 void addEventListener(const Reference<XConnection>& _rxConnection, TConnectionMap::iterator const & _rIter);
313 };
314
OSharedConnectionManager(const Reference<XComponentContext> & _rxContext)315 OSharedConnectionManager::OSharedConnectionManager(const Reference< XComponentContext >& _rxContext)
316 {
317 m_xProxyFactory.set( ProxyFactory::create( _rxContext ) );
318 }
319
~OSharedConnectionManager()320 OSharedConnectionManager::~OSharedConnectionManager()
321 {
322 }
323
disposing(const css::lang::EventObject & Source)324 void SAL_CALL OSharedConnectionManager::disposing( const css::lang::EventObject& Source )
325 {
326 MutexGuard aGuard(m_aMutex);
327 Reference<XConnection> xConnection(Source.Source,UNO_QUERY);
328 TSharedConnectionMap::const_iterator aFind = m_aSharedConnection.find(xConnection);
329 if ( m_aSharedConnection.end() != aFind )
330 {
331 osl_atomic_decrement(&aFind->second->second.nALiveCount);
332 if ( !aFind->second->second.nALiveCount )
333 {
334 ::comphelper::disposeComponent(aFind->second->second.xMasterConnection);
335 m_aConnections.erase(aFind->second);
336 }
337 m_aSharedConnection.erase(aFind);
338 }
339 }
340
getConnection(const OUString & url,const OUString & user,const OUString & password,const Sequence<PropertyValue> & _aInfo,ODatabaseSource * _pDataSource)341 Reference<XConnection> OSharedConnectionManager::getConnection( const OUString& url,
342 const OUString& user,
343 const OUString& password,
344 const Sequence< PropertyValue >& _aInfo,
345 ODatabaseSource* _pDataSource)
346 {
347 MutexGuard aGuard(m_aMutex);
348 TConnectionMap::key_type nId;
349 Sequence< PropertyValue > aInfoCopy(_aInfo);
350 sal_Int32 nPos = aInfoCopy.getLength();
351 aInfoCopy.realloc( nPos + 2 );
352 aInfoCopy[nPos].Name = "TableFilter";
353 aInfoCopy[nPos++].Value <<= _pDataSource->m_pImpl->m_aTableFilter;
354 aInfoCopy[nPos].Name = "TableTypeFilter";
355 aInfoCopy[nPos++].Value <<= _pDataSource->m_pImpl->m_aTableTypeFilter;
356
357 OUString sUser = user;
358 OUString sPassword = password;
359 if ((sUser.isEmpty()) && (sPassword.isEmpty()) && (!_pDataSource->m_pImpl->m_sUser.isEmpty()))
360 { // ease the usage of this method. data source which are intended to have a user automatically
361 // fill in the user/password combination if the caller of this method does not specify otherwise
362 sUser = _pDataSource->m_pImpl->m_sUser;
363 if (!_pDataSource->m_pImpl->m_aPassword.isEmpty())
364 sPassword = _pDataSource->m_pImpl->m_aPassword;
365 }
366
367 ::connectivity::OConnectionWrapper::createUniqueId(url,aInfoCopy,nId.m_pBuffer,sUser,sPassword);
368 TConnectionMap::iterator aIter = m_aConnections.find(nId);
369
370 if ( m_aConnections.end() == aIter )
371 {
372 TConnectionHolder aHolder;
373 aHolder.nALiveCount = 0; // will be incremented by addListener
374 aHolder.xMasterConnection = _pDataSource->buildIsolatedConnection(user,password);
375 aIter = m_aConnections.emplace(nId,aHolder).first;
376 }
377
378 Reference<XConnection> xRet;
379 if ( aIter->second.xMasterConnection.is() )
380 {
381 Reference< XAggregation > xConProxy = m_xProxyFactory->createProxy(aIter->second.xMasterConnection);
382 xRet = new OSharedConnection(xConProxy);
383 m_aSharedConnection.emplace(xRet,aIter);
384 addEventListener(xRet,aIter);
385 }
386
387 return xRet;
388 }
389
addEventListener(const Reference<XConnection> & _rxConnection,TConnectionMap::iterator const & _rIter)390 void OSharedConnectionManager::addEventListener(const Reference<XConnection>& _rxConnection, TConnectionMap::iterator const & _rIter)
391 {
392 Reference<XComponent> xComp(_rxConnection,UNO_QUERY);
393 xComp->addEventListener(this);
394 OSL_ENSURE( m_aConnections.end() != _rIter , "Iterator is end!");
395 osl_atomic_increment(&_rIter->second.nALiveCount);
396 }
397
398 namespace
399 {
lcl_filterDriverProperties(const Reference<XDriver> & _xDriver,const OUString & _sUrl,const Sequence<PropertyValue> & _rDataSourceSettings,const AsciiPropertyValue * _pKnownSettings)400 Sequence< PropertyValue > lcl_filterDriverProperties( const Reference< XDriver >& _xDriver, const OUString& _sUrl,
401 const Sequence< PropertyValue >& _rDataSourceSettings, const AsciiPropertyValue* _pKnownSettings )
402 {
403 if ( _xDriver.is() )
404 {
405 Sequence< DriverPropertyInfo > aDriverInfo(_xDriver->getPropertyInfo(_sUrl,_rDataSourceSettings));
406
407 const PropertyValue* pDataSourceSetting = _rDataSourceSettings.getConstArray();
408 const PropertyValue* pEnd = pDataSourceSetting + _rDataSourceSettings.getLength();
409
410 std::vector< PropertyValue > aRet;
411
412 for ( ; pDataSourceSetting != pEnd ; ++pDataSourceSetting )
413 {
414 bool bAllowSetting = false;
415 const AsciiPropertyValue* pSetting = _pKnownSettings;
416 for ( ; pSetting->AsciiName; ++pSetting )
417 {
418 if ( pDataSourceSetting->Name.equalsAscii( pSetting->AsciiName ) )
419 { // the particular data source setting is known
420
421 const DriverPropertyInfo* pAllowedDriverSetting = aDriverInfo.getConstArray();
422 const DriverPropertyInfo* pDriverSettingsEnd = pAllowedDriverSetting + aDriverInfo.getLength();
423 for ( ; pAllowedDriverSetting != pDriverSettingsEnd; ++pAllowedDriverSetting )
424 {
425 if ( pAllowedDriverSetting->Name.equalsAscii( pSetting->AsciiName ) )
426 { // the driver also allows this setting
427 bAllowSetting = true;
428 break;
429 }
430 }
431 break;
432 }
433 }
434 if ( bAllowSetting || !pSetting->AsciiName )
435 { // if the driver allows this particular setting, or if the setting is completely unknown,
436 // we pass it to the driver
437 aRet.push_back( *pDataSourceSetting );
438 }
439 }
440 if ( !aRet.empty() )
441 return comphelper::containerToSequence(aRet);
442 }
443 return Sequence< PropertyValue >();
444 }
445
446 typedef std::map< OUString, sal_Int32 > PropertyAttributeCache;
447
448 struct IsDefaultAndNotRemoveable
449 {
450 private:
451 const PropertyAttributeCache& m_rAttribs;
452
453 public:
IsDefaultAndNotRemoveabledbaccess::__anon5ce340130311::IsDefaultAndNotRemoveable454 explicit IsDefaultAndNotRemoveable( const PropertyAttributeCache& _rAttribs ) : m_rAttribs( _rAttribs ) { }
455
operator ()dbaccess::__anon5ce340130311::IsDefaultAndNotRemoveable456 bool operator()( const PropertyValue& _rProp )
457 {
458 if ( _rProp.State != PropertyState_DEFAULT_VALUE )
459 return false;
460
461 bool bRemoveable = true;
462
463 PropertyAttributeCache::const_iterator pos = m_rAttribs.find( _rProp.Name );
464 OSL_ENSURE( pos != m_rAttribs.end(), "IsDefaultAndNotRemoveable: illegal property name!" );
465 if ( pos != m_rAttribs.end() )
466 bRemoveable = ( ( pos->second & PropertyAttribute::REMOVABLE ) != 0 );
467
468 return !bRemoveable;
469 }
470 };
471 }
472
473
ODatabaseSource(const::rtl::Reference<ODatabaseModelImpl> & _pImpl)474 ODatabaseSource::ODatabaseSource(const ::rtl::Reference<ODatabaseModelImpl>& _pImpl)
475 :ModelDependentComponent( _pImpl )
476 ,ODatabaseSource_Base( getMutex() )
477 ,OPropertySetHelper( ODatabaseSource_Base::rBHelper )
478 , m_Bookmarks(*this, getMutex())
479 ,m_aFlushListeners( getMutex() )
480 {
481 // some kind of default
482 SAL_INFO("dbaccess", "DS: ctor: " << std::hex << this << ": " << std::hex << m_pImpl.get() );
483 }
484
~ODatabaseSource()485 ODatabaseSource::~ODatabaseSource()
486 {
487 SAL_INFO("dbaccess", "DS: dtor: " << std::hex << this << ": " << std::hex << m_pImpl.get() );
488 if ( !ODatabaseSource_Base::rBHelper.bInDispose && !ODatabaseSource_Base::rBHelper.bDisposed )
489 {
490 acquire();
491 dispose();
492 }
493 }
494
setName(const Reference<XDocumentDataSource> & _rxDocument,const OUString & _rNewName,DBContextAccess)495 void ODatabaseSource::setName( const Reference< XDocumentDataSource >& _rxDocument, const OUString& _rNewName, DBContextAccess )
496 {
497 ODatabaseSource& rModelImpl = dynamic_cast< ODatabaseSource& >( *_rxDocument );
498
499 SolarMutexGuard g;
500 if ( rModelImpl.m_pImpl.is() )
501 rModelImpl.m_pImpl->m_sName = _rNewName;
502 }
503
504 // css::lang::XTypeProvider
getTypes()505 Sequence< Type > ODatabaseSource::getTypes()
506 {
507 OTypeCollection aPropertyHelperTypes( cppu::UnoType<XFastPropertySet>::get(),
508 cppu::UnoType<XPropertySet>::get(),
509 cppu::UnoType<XMultiPropertySet>::get());
510
511 return ::comphelper::concatSequences(
512 ODatabaseSource_Base::getTypes(),
513 aPropertyHelperTypes.getTypes()
514 );
515 }
516
getImplementationId()517 Sequence< sal_Int8 > ODatabaseSource::getImplementationId()
518 {
519 return css::uno::Sequence<sal_Int8>();
520 }
521
522 // css::uno::XInterface
queryInterface(const Type & rType)523 Any ODatabaseSource::queryInterface( const Type & rType )
524 {
525 Any aIface = ODatabaseSource_Base::queryInterface( rType );
526 if ( !aIface.hasValue() )
527 aIface = ::cppu::OPropertySetHelper::queryInterface( rType );
528 return aIface;
529 }
530
acquire()531 void ODatabaseSource::acquire() noexcept
532 {
533 ODatabaseSource_Base::acquire();
534 }
535
release()536 void ODatabaseSource::release() noexcept
537 {
538 ODatabaseSource_Base::release();
539 }
540
disposing(const css::lang::EventObject & Source)541 void SAL_CALL ODatabaseSource::disposing( const css::lang::EventObject& Source )
542 {
543 if ( m_pImpl.is() )
544 m_pImpl->disposing(Source);
545 }
546
547 // XServiceInfo
getImplementationName()548 OUString ODatabaseSource::getImplementationName( )
549 {
550 return "com.sun.star.comp.dba.ODatabaseSource";
551 }
552
getSupportedServiceNames()553 Sequence< OUString > ODatabaseSource::getSupportedServiceNames( )
554 {
555 return { SERVICE_SDB_DATASOURCE, "com.sun.star.sdb.DocumentDataSource" };
556 }
557
supportsService(const OUString & _rServiceName)558 sal_Bool ODatabaseSource::supportsService( const OUString& _rServiceName )
559 {
560 return cppu::supportsService(this, _rServiceName);
561 }
562
563 // OComponentHelper
disposing()564 void ODatabaseSource::disposing()
565 {
566 SAL_INFO("dbaccess", "DS: disp: " << std::hex << this << ", " << std::hex << m_pImpl.get() );
567 ODatabaseSource_Base::WeakComponentImplHelperBase::disposing();
568 OPropertySetHelper::disposing();
569
570 EventObject aDisposeEvent(static_cast<XWeak*>(this));
571 m_aFlushListeners.disposeAndClear( aDisposeEvent );
572
573 ODatabaseDocument::clearObjectContainer(m_pImpl->m_xCommandDefinitions);
574 ODatabaseDocument::clearObjectContainer(m_pImpl->m_xTableDefinitions);
575 m_pImpl.clear();
576 }
577
GetFrameWeld()578 weld::Window* ODatabaseModelImpl::GetFrameWeld()
579 {
580 if (m_xDialogParent.is())
581 return Application::GetFrameWeld(m_xDialogParent);
582
583 Reference<XModel> xModel = getModel_noCreate();
584 if (!xModel.is())
585 return nullptr;
586 Reference<XController> xController(xModel->getCurrentController());
587 if (!xController.is())
588 return nullptr;
589 Reference<XFrame> xFrame(xController->getFrame());
590 if (!xFrame.is())
591 return nullptr;
592 Reference<css::awt::XWindow> xWindow(xFrame->getContainerWindow());
593 return Application::GetFrameWeld(xWindow);
594 }
595
buildLowLevelConnection(const OUString & _rUid,const OUString & _rPwd)596 Reference< XConnection > ODatabaseSource::buildLowLevelConnection(const OUString& _rUid, const OUString& _rPwd)
597 {
598 Reference< XConnection > xReturn;
599
600 Reference< XDriverManager > xManager;
601
602 #if ENABLE_FIREBIRD_SDBC
603 bool bIgnoreMigration = false;
604 bool bNeedMigration = false;
605 Reference< XModel > xModel = m_pImpl->getModel_noCreate();
606 if ( xModel)
607 {
608 //See ODbTypeWizDialogSetup::SaveDatabaseDocument
609 ::comphelper::NamedValueCollection aArgs( xModel->getArgs() );
610 aArgs.get("IgnoreFirebirdMigration") >>= bIgnoreMigration;
611 }
612 else
613 {
614 //ignore when we don't have a model. E.g. Mailmerge, data sources, fields...
615 bIgnoreMigration = true;
616 }
617
618 if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
619 bIgnoreMigration = true;
620
621 if(!bIgnoreMigration && m_pImpl->m_sConnectURL == "sdbc:embedded:hsqldb")
622 {
623 Reference<XStorage> const xRootStorage = m_pImpl->getOrCreateRootStorage();
624 OUString sMigrEnvVal;
625 osl_getEnvironment(OUString("DBACCESS_HSQL_MIGRATION").pData,
626 &sMigrEnvVal.pData);
627 if(!sMigrEnvVal.isEmpty())
628 bNeedMigration = true;
629 else
630 {
631 Reference<XPropertySet> const xPropSet(xRootStorage, UNO_QUERY_THROW);
632 sal_Int32 nOpenMode(0);
633 if ((xPropSet->getPropertyValue("OpenMode") >>= nOpenMode)
634 && (nOpenMode & css::embed::ElementModes::WRITE)
635 && (!Application::IsHeadlessModeEnabled()))
636 {
637 MigrationWarnDialog aWarnDlg(m_pImpl->GetFrameWeld());
638 bNeedMigration = aWarnDlg.run() == RET_OK;
639 }
640 }
641 if (bNeedMigration)
642 {
643 // back up content xml file if migration was successful
644 static constexpr OUStringLiteral BACKUP_XML_NAME = u"content_before_migration.xml";
645 try
646 {
647 if(xRootStorage->isStreamElement(BACKUP_XML_NAME))
648 xRootStorage->removeElement(BACKUP_XML_NAME);
649 }
650 catch (NoSuchElementException&)
651 {
652 SAL_INFO("dbaccess", "No file content_before_migration.xml found" );
653 }
654 xRootStorage->copyElementTo("content.xml", xRootStorage,
655 BACKUP_XML_NAME);
656
657 m_pImpl->m_sConnectURL = "sdbc:embedded:firebird";
658 }
659 }
660 #endif
661
662 try {
663 xManager.set( ConnectionPool::create( m_pImpl->m_aContext ), UNO_QUERY_THROW );
664 } catch( const Exception& ) { }
665 if ( !xManager.is() )
666 // no connection pool installed, fall back to driver manager
667 xManager.set( DriverManager::create(m_pImpl->m_aContext ), UNO_QUERY_THROW );
668
669 OUString sUser(_rUid);
670 OUString sPwd(_rPwd);
671 if ((sUser.isEmpty()) && (sPwd.isEmpty()) && (!m_pImpl->m_sUser.isEmpty()))
672 { // ease the usage of this method. data source which are intended to have a user automatically
673 // fill in the user/password combination if the caller of this method does not specify otherwise
674 sUser = m_pImpl->m_sUser;
675 if (!m_pImpl->m_aPassword.isEmpty())
676 sPwd = m_pImpl->m_aPassword;
677 }
678
679 const char* pExceptionMessageId = RID_STR_COULDNOTCONNECT_UNSPECIFIED;
680 if (xManager.is())
681 {
682 sal_Int32 nAdditionalArgs(0);
683 if (!sUser.isEmpty()) ++nAdditionalArgs;
684 if (!sPwd.isEmpty()) ++nAdditionalArgs;
685
686 Sequence< PropertyValue > aUserPwd(nAdditionalArgs);
687 sal_Int32 nArgPos = 0;
688 if (!sUser.isEmpty())
689 {
690 aUserPwd[ nArgPos ].Name = "user";
691 aUserPwd[ nArgPos ].Value <<= sUser;
692 ++nArgPos;
693 }
694 if (!sPwd.isEmpty())
695 {
696 aUserPwd[ nArgPos ].Name = "password";
697 aUserPwd[ nArgPos ].Value <<= sPwd;
698 }
699 Reference< XDriver > xDriver;
700 try
701 {
702
703 // choose driver
704 Reference< XDriverAccess > xAccessDrivers( xManager, UNO_QUERY );
705 if ( xAccessDrivers.is() )
706 xDriver = xAccessDrivers->getDriverByURL( m_pImpl->m_sConnectURL );
707 }
708 catch( const Exception& )
709 {
710 TOOLS_WARN_EXCEPTION("dbaccess", "ODatabaseSource::buildLowLevelConnection: got a strange exception while analyzing the error" );
711 }
712 if ( !xDriver.is() || !xDriver->acceptsURL( m_pImpl->m_sConnectURL ) )
713 {
714 // Nowadays, it's allowed for a driver to be registered for a given URL, but actually not to accept it.
715 // This is because registration nowadays happens at compile time (by adding respective configuration data),
716 // but acceptance is decided at runtime.
717 pExceptionMessageId = RID_STR_COULDNOTCONNECT_NODRIVER;
718 }
719 else
720 {
721 Sequence< PropertyValue > aDriverInfo = lcl_filterDriverProperties(
722 xDriver,
723 m_pImpl->m_sConnectURL,
724 m_pImpl->m_xSettings->getPropertyValues(),
725 dbaccess::ODatabaseModelImpl::getDefaultDataSourceSettings()
726 );
727
728 if ( m_pImpl->isEmbeddedDatabase() )
729 {
730 sal_Int32 nCount = aDriverInfo.getLength();
731 aDriverInfo.realloc(nCount + 3 );
732
733 aDriverInfo[nCount].Name = "URL";
734 aDriverInfo[nCount++].Value <<= m_pImpl->getURL();
735
736 aDriverInfo[nCount].Name = "Storage";
737 Reference< css::document::XDocumentSubStorageSupplier> xDocSup( m_pImpl->getDocumentSubStorageSupplier() );
738 aDriverInfo[nCount++].Value <<= xDocSup->getDocumentSubStorage("database",ElementModes::READWRITE);
739
740 aDriverInfo[nCount].Name = "Document";
741 aDriverInfo[nCount++].Value <<= getDatabaseDocument();
742 }
743 if (nAdditionalArgs)
744 xReturn = xManager->getConnectionWithInfo(m_pImpl->m_sConnectURL, ::comphelper::concatSequences(aUserPwd,aDriverInfo));
745 else
746 xReturn = xManager->getConnectionWithInfo(m_pImpl->m_sConnectURL,aDriverInfo);
747
748 if ( m_pImpl->isEmbeddedDatabase() )
749 {
750 // see ODatabaseSource::flushed for comment on why we register as FlushListener
751 // at the connection
752 Reference< XFlushable > xFlushable( xReturn, UNO_QUERY );
753 if ( xFlushable.is() )
754 FlushNotificationAdapter::installAdapter( xFlushable, this );
755 }
756 }
757 }
758 else
759 pExceptionMessageId = RID_STR_COULDNOTLOAD_MANAGER;
760
761 if ( !xReturn.is() )
762 {
763 OUString sMessage = DBA_RES(pExceptionMessageId)
764 .replaceAll("$name$", m_pImpl->m_sConnectURL);
765
766 SQLContext aContext;
767 aContext.Message = DBA_RES(RID_STR_CONNECTION_REQUEST).
768 replaceFirst("$name$", m_pImpl->m_sConnectURL);
769
770 throwGenericSQLException( sMessage, static_cast< XDataSource* >( this ), makeAny( aContext ) );
771 }
772
773 #if ENABLE_FIREBIRD_SDBC
774 if( bNeedMigration )
775 {
776 Reference< css::document::XDocumentSubStorageSupplier> xDocSup(
777 m_pImpl->getDocumentSubStorageSupplier() );
778 dbahsql::HsqlImporter importer(xReturn,
779 xDocSup->getDocumentSubStorage("database",ElementModes::READWRITE) );
780 importer.importHsqlDatabase(m_pImpl->GetFrameWeld());
781 }
782 #endif
783
784 return xReturn;
785 }
786
787 // OPropertySetHelper
getPropertySetInfo()788 Reference< XPropertySetInfo > ODatabaseSource::getPropertySetInfo()
789 {
790 return createPropertySetInfo( getInfoHelper() ) ;
791 }
792
793 // comphelper::OPropertyArrayUsageHelper
createArrayHelper() const794 ::cppu::IPropertyArrayHelper* ODatabaseSource::createArrayHelper( ) const
795 {
796 BEGIN_PROPERTY_HELPER(13)
797 DECL_PROP1(INFO, Sequence< PropertyValue >, BOUND);
798 DECL_PROP1_BOOL(ISPASSWORDREQUIRED, BOUND);
799 DECL_PROP1_BOOL(ISREADONLY, READONLY);
800 DECL_PROP1(LAYOUTINFORMATION, Sequence< PropertyValue >, BOUND);
801 DECL_PROP1(NAME, OUString, READONLY);
802 DECL_PROP2_IFACE(NUMBERFORMATSSUPPLIER, XNumberFormatsSupplier, READONLY, TRANSIENT);
803 DECL_PROP1(PASSWORD, OUString, TRANSIENT);
804 DECL_PROP2_IFACE(SETTINGS, XPropertySet, BOUND, READONLY);
805 DECL_PROP1_BOOL(SUPPRESSVERSIONCL, BOUND);
806 DECL_PROP1(TABLEFILTER, Sequence< OUString >,BOUND);
807 DECL_PROP1(TABLETYPEFILTER, Sequence< OUString >,BOUND);
808 DECL_PROP1(URL, OUString, BOUND);
809 DECL_PROP1(USER, OUString, BOUND);
810 END_PROPERTY_HELPER();
811 }
812
813 // cppu::OPropertySetHelper
getInfoHelper()814 ::cppu::IPropertyArrayHelper& ODatabaseSource::getInfoHelper()
815 {
816 return *getArrayHelper();
817 }
818
convertFastPropertyValue(Any & rConvertedValue,Any & rOldValue,sal_Int32 nHandle,const Any & rValue)819 sal_Bool ODatabaseSource::convertFastPropertyValue(Any & rConvertedValue, Any & rOldValue, sal_Int32 nHandle, const Any& rValue )
820 {
821 bool bModified(false);
822 if ( m_pImpl.is() )
823 {
824 switch (nHandle)
825 {
826 case PROPERTY_ID_TABLEFILTER:
827 bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aTableFilter);
828 break;
829 case PROPERTY_ID_TABLETYPEFILTER:
830 bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aTableTypeFilter);
831 break;
832 case PROPERTY_ID_USER:
833 bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_sUser);
834 break;
835 case PROPERTY_ID_PASSWORD:
836 bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aPassword);
837 break;
838 case PROPERTY_ID_ISPASSWORDREQUIRED:
839 bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_bPasswordRequired);
840 break;
841 case PROPERTY_ID_SUPPRESSVERSIONCL:
842 bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_bSuppressVersionColumns);
843 break;
844 case PROPERTY_ID_LAYOUTINFORMATION:
845 bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_aLayoutInformation);
846 break;
847 case PROPERTY_ID_URL:
848 {
849 bModified = ::comphelper::tryPropertyValue(rConvertedValue, rOldValue, rValue, m_pImpl->m_sConnectURL);
850 } break;
851 case PROPERTY_ID_INFO:
852 {
853 Sequence<PropertyValue> aValues;
854 if (!(rValue >>= aValues))
855 throw IllegalArgumentException();
856
857 for ( auto const & checkName : std::as_const(aValues) )
858 {
859 if ( checkName.Name.isEmpty() )
860 throw IllegalArgumentException();
861 }
862
863 Sequence< PropertyValue > aSettings = m_pImpl->m_xSettings->getPropertyValues();
864 bModified = aSettings.getLength() != aValues.getLength();
865 if ( !bModified )
866 {
867 const PropertyValue* pInfoIter = aSettings.getConstArray();
868 const PropertyValue* checkValue = aValues.getConstArray();
869 for ( ;!bModified && checkValue != aValues.end() ; ++checkValue,++pInfoIter)
870 {
871 bModified = checkValue->Name != pInfoIter->Name;
872 if ( !bModified )
873 {
874 bModified = checkValue->Value != pInfoIter->Value;
875 }
876 }
877 }
878
879 rConvertedValue = rValue;
880 rOldValue <<= aSettings;
881 }
882 break;
883 default:
884 SAL_WARN("dbaccess", "ODatabaseSource::convertFastPropertyValue: unknown or readonly Property!" );
885 }
886 }
887 return bModified;
888 }
889
890 namespace
891 {
892 struct SelectPropertyName
893 {
894 public:
operator ()dbaccess::__anon5ce340130411::SelectPropertyName895 const OUString& operator()( const PropertyValue& _lhs )
896 {
897 return _lhs.Name;
898 }
899 };
900
901 /** sets a new set of property values for a given property bag instance
902
903 The method takes a property bag, and a sequence of property values to set for this bag.
904 Upon return, every property which is not part of the given sequence is
905 <ul><li>removed from the bag, if it's a removable property</li>
906 <li><em>or</em>reset to its default value, if it's not a removable property</li>
907 </ul>.
908
909 @param _rxPropertyBag
910 the property bag to operate on
911 @param _rAllNewPropertyValues
912 the new property values to set for the bag
913 */
lcl_setPropertyValues_resetOrRemoveOther(const Reference<XPropertyBag> & _rxPropertyBag,const Sequence<PropertyValue> & _rAllNewPropertyValues)914 void lcl_setPropertyValues_resetOrRemoveOther( const Reference< XPropertyBag >& _rxPropertyBag, const Sequence< PropertyValue >& _rAllNewPropertyValues )
915 {
916 // sequences are ugly to operate on
917 std::set<OUString> aToBeSetPropertyNames;
918 std::transform(
919 _rAllNewPropertyValues.begin(),
920 _rAllNewPropertyValues.end(),
921 std::inserter( aToBeSetPropertyNames, aToBeSetPropertyNames.end() ),
922 SelectPropertyName()
923 );
924
925 try
926 {
927 // obtain all properties currently known at the bag
928 Reference< XPropertySetInfo > xPSI( _rxPropertyBag->getPropertySetInfo(), UNO_SET_THROW );
929 const Sequence< Property > aAllExistentProperties( xPSI->getProperties() );
930
931 Reference< XPropertyState > xPropertyState( _rxPropertyBag, UNO_QUERY_THROW );
932
933 // loop through them, and reset resp. default properties which are not to be set
934 for ( auto const & existentProperty : aAllExistentProperties )
935 {
936 if ( aToBeSetPropertyNames.find( existentProperty.Name ) != aToBeSetPropertyNames.end() )
937 continue;
938
939 // this property is not to be set, but currently exists in the bag.
940 // -> Remove it, or reset it to the default.
941 if ( ( existentProperty.Attributes & PropertyAttribute::REMOVABLE ) != 0 )
942 _rxPropertyBag->removeProperty( existentProperty.Name );
943 else
944 xPropertyState->setPropertyToDefault( existentProperty.Name );
945 }
946
947 // finally, set the new property values
948 _rxPropertyBag->setPropertyValues( _rAllNewPropertyValues );
949 }
950 catch( const Exception& )
951 {
952 DBG_UNHANDLED_EXCEPTION("dbaccess");
953 }
954 }
955 }
956
setFastPropertyValue_NoBroadcast(sal_Int32 nHandle,const Any & rValue)957 void ODatabaseSource::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const Any& rValue )
958 {
959 if ( !m_pImpl.is() )
960 return;
961
962 switch(nHandle)
963 {
964 case PROPERTY_ID_TABLEFILTER:
965 rValue >>= m_pImpl->m_aTableFilter;
966 break;
967 case PROPERTY_ID_TABLETYPEFILTER:
968 rValue >>= m_pImpl->m_aTableTypeFilter;
969 break;
970 case PROPERTY_ID_USER:
971 rValue >>= m_pImpl->m_sUser;
972 // if the user name has changed, reset the password
973 m_pImpl->m_aPassword.clear();
974 break;
975 case PROPERTY_ID_PASSWORD:
976 rValue >>= m_pImpl->m_aPassword;
977 break;
978 case PROPERTY_ID_ISPASSWORDREQUIRED:
979 m_pImpl->m_bPasswordRequired = any2bool(rValue);
980 break;
981 case PROPERTY_ID_SUPPRESSVERSIONCL:
982 m_pImpl->m_bSuppressVersionColumns = any2bool(rValue);
983 break;
984 case PROPERTY_ID_URL:
985 rValue >>= m_pImpl->m_sConnectURL;
986 break;
987 case PROPERTY_ID_INFO:
988 {
989 Sequence< PropertyValue > aInfo;
990 OSL_VERIFY( rValue >>= aInfo );
991 lcl_setPropertyValues_resetOrRemoveOther( m_pImpl->m_xSettings, aInfo );
992 }
993 break;
994 case PROPERTY_ID_LAYOUTINFORMATION:
995 rValue >>= m_pImpl->m_aLayoutInformation;
996 break;
997 }
998 m_pImpl->setModified(true);
999 }
1000
getFastPropertyValue(Any & rValue,sal_Int32 nHandle) const1001 void ODatabaseSource::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
1002 {
1003 if ( !m_pImpl.is() )
1004 return;
1005
1006 switch (nHandle)
1007 {
1008 case PROPERTY_ID_TABLEFILTER:
1009 rValue <<= m_pImpl->m_aTableFilter;
1010 break;
1011 case PROPERTY_ID_TABLETYPEFILTER:
1012 rValue <<= m_pImpl->m_aTableTypeFilter;
1013 break;
1014 case PROPERTY_ID_USER:
1015 rValue <<= m_pImpl->m_sUser;
1016 break;
1017 case PROPERTY_ID_PASSWORD:
1018 rValue <<= m_pImpl->m_aPassword;
1019 break;
1020 case PROPERTY_ID_ISPASSWORDREQUIRED:
1021 rValue <<= m_pImpl->m_bPasswordRequired;
1022 break;
1023 case PROPERTY_ID_SUPPRESSVERSIONCL:
1024 rValue <<= m_pImpl->m_bSuppressVersionColumns;
1025 break;
1026 case PROPERTY_ID_ISREADONLY:
1027 rValue <<= m_pImpl->m_bReadOnly;
1028 break;
1029 case PROPERTY_ID_INFO:
1030 {
1031 try
1032 {
1033 // collect the property attributes of all current settings
1034 Reference< XPropertySet > xSettingsAsProps( m_pImpl->m_xSettings, UNO_QUERY_THROW );
1035 Reference< XPropertySetInfo > xPST( xSettingsAsProps->getPropertySetInfo(), UNO_SET_THROW );
1036 const Sequence< Property > aSettings( xPST->getProperties() );
1037 std::map< OUString, sal_Int32 > aPropertyAttributes;
1038 for ( auto const & setting : aSettings )
1039 {
1040 aPropertyAttributes[ setting.Name ] = setting.Attributes;
1041 }
1042
1043 // get all current settings with their values
1044 Sequence< PropertyValue > aValues( m_pImpl->m_xSettings->getPropertyValues() );
1045
1046 // transform them so that only property values which fulfill certain
1047 // criteria survive
1048 Sequence< PropertyValue > aNonDefaultOrUserDefined( aValues.getLength() );
1049 const PropertyValue* pCopyEnd = std::remove_copy_if(
1050 aValues.begin(),
1051 aValues.end(),
1052 aNonDefaultOrUserDefined.getArray(),
1053 IsDefaultAndNotRemoveable( aPropertyAttributes )
1054 );
1055 aNonDefaultOrUserDefined.realloc( pCopyEnd - aNonDefaultOrUserDefined.getArray() );
1056 rValue <<= aNonDefaultOrUserDefined;
1057 }
1058 catch( const Exception& )
1059 {
1060 DBG_UNHANDLED_EXCEPTION("dbaccess");
1061 }
1062 }
1063 break;
1064 case PROPERTY_ID_SETTINGS:
1065 rValue <<= m_pImpl->m_xSettings;
1066 break;
1067 case PROPERTY_ID_URL:
1068 rValue <<= m_pImpl->m_sConnectURL;
1069 break;
1070 case PROPERTY_ID_NUMBERFORMATSSUPPLIER:
1071 rValue <<= m_pImpl->getNumberFormatsSupplier();
1072 break;
1073 case PROPERTY_ID_NAME:
1074 rValue <<= m_pImpl->m_sName;
1075 break;
1076 case PROPERTY_ID_LAYOUTINFORMATION:
1077 rValue <<= m_pImpl->m_aLayoutInformation;
1078 break;
1079 default:
1080 SAL_WARN("dbaccess","unknown Property");
1081 }
1082 }
1083
1084 // XDataSource
setLoginTimeout(sal_Int32 seconds)1085 void ODatabaseSource::setLoginTimeout(sal_Int32 seconds)
1086 {
1087 ModelMethodGuard aGuard( *this );
1088 m_pImpl->m_nLoginTimeout = seconds;
1089 }
1090
getLoginTimeout()1091 sal_Int32 ODatabaseSource::getLoginTimeout()
1092 {
1093 ModelMethodGuard aGuard( *this );
1094 return m_pImpl->m_nLoginTimeout;
1095 }
1096
1097 // XCompletedConnection
connectWithCompletion(const Reference<XInteractionHandler> & _rxHandler)1098 Reference< XConnection > SAL_CALL ODatabaseSource::connectWithCompletion( const Reference< XInteractionHandler >& _rxHandler )
1099 {
1100 return connectWithCompletion(_rxHandler,false);
1101 }
1102
getConnection(const OUString & user,const OUString & password)1103 Reference< XConnection > ODatabaseSource::getConnection(const OUString& user, const OUString& password)
1104 {
1105 return getConnection(user,password,false);
1106 }
1107
getIsolatedConnection(const OUString & user,const OUString & password)1108 Reference< XConnection > SAL_CALL ODatabaseSource::getIsolatedConnection( const OUString& user, const OUString& password )
1109 {
1110 return getConnection(user,password,true);
1111 }
1112
getIsolatedConnectionWithCompletion(const Reference<XInteractionHandler> & _rxHandler)1113 Reference< XConnection > SAL_CALL ODatabaseSource::getIsolatedConnectionWithCompletion( const Reference< XInteractionHandler >& _rxHandler )
1114 {
1115 return connectWithCompletion(_rxHandler,true);
1116 }
1117
connectWithCompletion(const Reference<XInteractionHandler> & _rxHandler,bool _bIsolated)1118 Reference< XConnection > ODatabaseSource::connectWithCompletion( const Reference< XInteractionHandler >& _rxHandler,bool _bIsolated )
1119 {
1120 ModelMethodGuard aGuard( *this );
1121
1122 if (!_rxHandler.is())
1123 {
1124 SAL_WARN("dbaccess","ODatabaseSource::connectWithCompletion: invalid interaction handler!");
1125 return getConnection(m_pImpl->m_sUser, m_pImpl->m_aPassword,_bIsolated);
1126 }
1127
1128 OUString sUser(m_pImpl->m_sUser), sPassword(m_pImpl->m_aPassword);
1129 bool bNewPasswordGiven = false;
1130
1131 if (m_pImpl->m_bPasswordRequired && sPassword.isEmpty())
1132 { // we need a password, but don't have one yet.
1133 // -> ask the user
1134
1135 // build an interaction request
1136 // two continuations (Ok and Cancel)
1137 rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
1138 rtl::Reference<OAuthenticationContinuation> pAuthenticate = new OAuthenticationContinuation;
1139
1140 // the name which should be referred in the login dialog
1141 OUString sServerName( m_pImpl->m_sName );
1142 INetURLObject aURLCheck( sServerName );
1143 if ( aURLCheck.GetProtocol() != INetProtocol::NotValid )
1144 sServerName = aURLCheck.getBase( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::Unambiguous );
1145
1146 // the request
1147 AuthenticationRequest aRequest;
1148 aRequest.ServerName = sServerName;
1149 aRequest.HasRealm = aRequest.HasAccount = false;
1150 aRequest.HasUserName = aRequest.HasPassword = true;
1151 aRequest.UserName = m_pImpl->m_sUser;
1152 aRequest.Password = m_pImpl->m_sFailedPassword.isEmpty() ? m_pImpl->m_aPassword : m_pImpl->m_sFailedPassword;
1153 rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest(makeAny(aRequest));
1154 // some knittings
1155 pRequest->addContinuation(pAbort);
1156 pRequest->addContinuation(pAuthenticate);
1157
1158 // handle the request
1159 try
1160 {
1161 _rxHandler->handle(pRequest);
1162 }
1163 catch(Exception&)
1164 {
1165 DBG_UNHANDLED_EXCEPTION("dbaccess");
1166 }
1167
1168 if (!pAuthenticate->wasSelected())
1169 return Reference< XConnection >();
1170
1171 // get the result
1172 sUser = m_pImpl->m_sUser = pAuthenticate->getUser();
1173 sPassword = pAuthenticate->getPassword();
1174
1175 if (pAuthenticate->getRememberPassword())
1176 {
1177 m_pImpl->m_aPassword = pAuthenticate->getPassword();
1178 bNewPasswordGiven = true;
1179 }
1180 m_pImpl->m_sFailedPassword.clear();
1181 }
1182
1183 try
1184 {
1185 return getConnection(sUser, sPassword,_bIsolated);
1186 }
1187 catch(Exception&)
1188 {
1189 if (bNewPasswordGiven)
1190 {
1191 m_pImpl->m_sFailedPassword = m_pImpl->m_aPassword;
1192 // assume that we had an authentication problem. Without this we may, after an unsuccessful connect, while
1193 // the user gave us a password and the order to remember it, never allow a password input again (at least
1194 // not without restarting the session)
1195 m_pImpl->m_aPassword.clear();
1196 }
1197 throw;
1198 }
1199 }
1200
buildIsolatedConnection(const OUString & user,const OUString & password)1201 Reference< XConnection > ODatabaseSource::buildIsolatedConnection(const OUString& user, const OUString& password)
1202 {
1203 Reference< XConnection > xConn;
1204 Reference< XConnection > xSdbcConn = buildLowLevelConnection(user, password);
1205 OSL_ENSURE( xSdbcConn.is(), "ODatabaseSource::buildIsolatedConnection: invalid return value of buildLowLevelConnection!" );
1206 // buildLowLevelConnection is expected to always succeed
1207 if ( xSdbcConn.is() )
1208 {
1209 // build a connection server and return it (no stubs)
1210 xConn = new OConnection(*this, xSdbcConn, m_pImpl->m_aContext);
1211 }
1212 return xConn;
1213 }
1214
getConnection(const OUString & user,const OUString & password,bool _bIsolated)1215 Reference< XConnection > ODatabaseSource::getConnection(const OUString& user, const OUString& password,bool _bIsolated)
1216 {
1217 ModelMethodGuard aGuard( *this );
1218
1219 Reference< XConnection > xConn;
1220 if ( _bIsolated )
1221 {
1222 xConn = buildIsolatedConnection(user,password);
1223 }
1224 else
1225 { // create a new proxy for the connection
1226 if ( !m_pImpl->m_xSharedConnectionManager.is() )
1227 {
1228 // TODO ideally we could just have one field, but to make that work
1229 // we'd need to move OSharedConnectionManager into its own file and header
1230 rtl::Reference<OSharedConnectionManager> manager =
1231 new OSharedConnectionManager( m_pImpl->m_aContext );
1232 m_pImpl->m_pSharedConnectionManager = manager.get();
1233 m_pImpl->m_xSharedConnectionManager = m_pImpl->m_pSharedConnectionManager;
1234 }
1235 xConn = m_pImpl->m_pSharedConnectionManager->getConnection(
1236 m_pImpl->m_sConnectURL, user, password, m_pImpl->m_xSettings->getPropertyValues(), this );
1237 }
1238
1239 if ( xConn.is() )
1240 {
1241 Reference< XComponent> xComp(xConn,UNO_QUERY);
1242 if ( xComp.is() )
1243 xComp->addEventListener( static_cast< XContainerListener* >( this ) );
1244 m_pImpl->m_aConnections.emplace_back(xConn);
1245 }
1246
1247 return xConn;
1248 }
1249
getBookmarks()1250 Reference< XNameAccess > SAL_CALL ODatabaseSource::getBookmarks( )
1251 {
1252 ModelMethodGuard aGuard( *this );
1253 // tdf#114596 this may look nutty but see OBookmarkContainer::acquire()
1254 return static_cast<XNameContainer*>(&m_Bookmarks);
1255 }
1256
getQueryDefinitions()1257 Reference< XNameAccess > SAL_CALL ODatabaseSource::getQueryDefinitions( )
1258 {
1259 ModelMethodGuard aGuard( *this );
1260
1261 Reference< XNameAccess > xContainer = m_pImpl->m_xCommandDefinitions;
1262 if ( !xContainer.is() )
1263 {
1264 Any aValue;
1265 css::uno::Reference< css::uno::XInterface > xMy(*this);
1266 if ( dbtools::getDataSourceSetting(xMy,"CommandDefinitions",aValue) )
1267 {
1268 OUString sSupportService;
1269 aValue >>= sSupportService;
1270 if ( !sSupportService.isEmpty() )
1271 {
1272 Sequence<Any> aArgs(1);
1273 aArgs[0] <<= NamedValue("DataSource",makeAny(xMy));
1274 xContainer.set( m_pImpl->m_aContext->getServiceManager()->createInstanceWithArgumentsAndContext(sSupportService, aArgs, m_pImpl->m_aContext), UNO_QUERY);
1275 }
1276 }
1277 if ( !xContainer.is() )
1278 {
1279 TContentPtr& rContainerData( m_pImpl->getObjectContainer( ODatabaseModelImpl::E_QUERY ) );
1280 xContainer = new OCommandContainer( m_pImpl->m_aContext, *this, rContainerData, false );
1281 }
1282 m_pImpl->m_xCommandDefinitions = xContainer;
1283 }
1284 return xContainer;
1285 }
1286
1287 // XTablesSupplier
getTables()1288 Reference< XNameAccess > ODatabaseSource::getTables()
1289 {
1290 ModelMethodGuard aGuard( *this );
1291
1292 Reference< XNameAccess > xContainer = m_pImpl->m_xTableDefinitions;
1293 if ( !xContainer.is() )
1294 {
1295 TContentPtr& rContainerData( m_pImpl->getObjectContainer( ODatabaseModelImpl::E_TABLE ) );
1296 xContainer = new OCommandContainer( m_pImpl->m_aContext, *this, rContainerData, true );
1297 m_pImpl->m_xTableDefinitions = xContainer;
1298 }
1299 return xContainer;
1300 }
1301
flush()1302 void SAL_CALL ODatabaseSource::flush( )
1303 {
1304 try
1305 {
1306 // SYNCHRONIZED ->
1307 {
1308 ModelMethodGuard aGuard( *this );
1309
1310 typedef ::utl::SharedUNOComponent< XModel, ::utl::CloseableComponent > SharedModel;
1311 SharedModel xModel( m_pImpl->getModel_noCreate(), SharedModel::NoTakeOwnership );
1312
1313 if ( !xModel.is() )
1314 xModel.reset( m_pImpl->createNewModel_deliverOwnership(), SharedModel::TakeOwnership );
1315
1316 Reference< css::frame::XStorable> xStorable( xModel, UNO_QUERY_THROW );
1317 xStorable->store();
1318 }
1319 // <- SYNCHRONIZED
1320
1321 css::lang::EventObject aFlushedEvent(*this);
1322 m_aFlushListeners.notifyEach( &XFlushListener::flushed, aFlushedEvent );
1323 }
1324 catch( const Exception& )
1325 {
1326 DBG_UNHANDLED_EXCEPTION("dbaccess");
1327 }
1328 }
1329
flushed(const EventObject &)1330 void SAL_CALL ODatabaseSource::flushed( const EventObject& /*rEvent*/ )
1331 {
1332 ModelMethodGuard aGuard( *this );
1333
1334 // Okay, this is some hack.
1335 //
1336 // In general, we have the problem that embedded databases write into their underlying storage, which
1337 // logically is one of our sub storage, and practically is a temporary file maintained by the
1338 // package implementation. As long as we did not commit this storage and our main storage,
1339 // the changes made by the embedded database engine are not really reflected in the database document
1340 // file. This is Bad (TM) for a "real" database application - imagine somebody entering some
1341 // data, and then crashing: For a database application, you would expect that the data still is present
1342 // when you connect to the database next time.
1343 //
1344 // Since this is a conceptual problem as long as we do use those ZIP packages (in fact, we *cannot*
1345 // provide the desired functionality as long as we do not have a package format which allows O(1) writes),
1346 // we cannot completely fix this. However, we can relax the problem by committing more often - often
1347 // enough so that data loss is more seldom, and seldom enough so that there's no noticeable performance
1348 // decrease.
1349 //
1350 // For this, we introduced a few places which XFlushable::flush their connections, and register as
1351 // XFlushListener at the embedded connection (which needs to provide the XFlushable functionality).
1352 // Then, when the connection is flushed, we commit both the database storage and our main storage.
1353 //
1354 // #i55274#
1355
1356 OSL_ENSURE( m_pImpl->isEmbeddedDatabase(), "ODatabaseSource::flushed: no embedded database?!" );
1357 bool bWasModified = m_pImpl->m_bModified;
1358 m_pImpl->commitEmbeddedStorage();
1359 m_pImpl->setModified( bWasModified );
1360 }
1361
addFlushListener(const Reference<css::util::XFlushListener> & _xListener)1362 void SAL_CALL ODatabaseSource::addFlushListener( const Reference< css::util::XFlushListener >& _xListener )
1363 {
1364 m_aFlushListeners.addInterface(_xListener);
1365 }
1366
removeFlushListener(const Reference<css::util::XFlushListener> & _xListener)1367 void SAL_CALL ODatabaseSource::removeFlushListener( const Reference< css::util::XFlushListener >& _xListener )
1368 {
1369 m_aFlushListeners.removeInterface(_xListener);
1370 }
1371
elementInserted(const ContainerEvent &)1372 void SAL_CALL ODatabaseSource::elementInserted( const ContainerEvent& /*Event*/ )
1373 {
1374 ModelMethodGuard aGuard( *this );
1375 if ( m_pImpl.is() )
1376 m_pImpl->setModified(true);
1377 }
1378
elementRemoved(const ContainerEvent &)1379 void SAL_CALL ODatabaseSource::elementRemoved( const ContainerEvent& /*Event*/ )
1380 {
1381 ModelMethodGuard aGuard( *this );
1382 if ( m_pImpl.is() )
1383 m_pImpl->setModified(true);
1384 }
1385
elementReplaced(const ContainerEvent &)1386 void SAL_CALL ODatabaseSource::elementReplaced( const ContainerEvent& /*Event*/ )
1387 {
1388 ModelMethodGuard aGuard( *this );
1389 if ( m_pImpl.is() )
1390 m_pImpl->setModified(true);
1391 }
1392
1393 // XDocumentDataSource
getDatabaseDocument()1394 Reference< XOfficeDatabaseDocument > SAL_CALL ODatabaseSource::getDatabaseDocument()
1395 {
1396 ModelMethodGuard aGuard( *this );
1397
1398 Reference< XModel > xModel( m_pImpl->getModel_noCreate() );
1399 if ( !xModel.is() )
1400 xModel = m_pImpl->createNewModel_deliverOwnership();
1401
1402 return Reference< XOfficeDatabaseDocument >( xModel, UNO_QUERY_THROW );
1403 }
1404
initialize(css::uno::Sequence<css::uno::Any> const & rArguments)1405 void SAL_CALL ODatabaseSource::initialize( css::uno::Sequence< css::uno::Any > const & rArguments)
1406 {
1407 ::comphelper::NamedValueCollection aProperties( rArguments );
1408 if (aProperties.has("ParentWindow"))
1409 aProperties.get("ParentWindow") >>= m_pImpl->m_xDialogParent;
1410 }
1411
getThis() const1412 Reference< XInterface > ODatabaseSource::getThis() const
1413 {
1414 return *const_cast< ODatabaseSource* >( this );
1415 }
1416
1417 } // namespace dbaccess
1418
1419 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
com_sun_star_comp_dba_ODatabaseSource(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)1420 com_sun_star_comp_dba_ODatabaseSource(css::uno::XComponentContext* context,
1421 css::uno::Sequence<css::uno::Any> const &)
1422 {
1423 css::uno::Reference<XInterface> inst(
1424 DatabaseContext::create(context)->createInstance());
1425 inst->acquire();
1426 return inst.get();
1427 }
1428
1429 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1430