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 <sal/config.h>
21 
22 #include <com/sun/star/lang/IllegalAccessException.hpp>
23 #include <com/sun/star/lang/IllegalArgumentException.hpp>
24 #include <com/sun/star/container/ElementExistException.hpp>
25 #include <com/sun/star/container/NoSuchElementException.hpp>
26 #include <com/sun/star/sdb/XDatabaseRegistrations.hpp>
27 
28 #include <cppuhelper/basemutex.hxx>
29 #include <comphelper/interfacecontainer2.hxx>
30 #include <cppuhelper/implbase1.hxx>
31 #include <rtl/ustrbuf.hxx>
32 #include <osl/diagnose.h>
33 #include <unotools/pathoptions.hxx>
34 #include <tools/urlobj.hxx>
35 #include <unotools/confignode.hxx>
36 
37 #include "databaseregistrations.hxx"
38 
39 namespace dbaccess
40 {
41     using ::com::sun::star::uno::Reference;
42     using ::com::sun::star::uno::RuntimeException;
43     using ::com::sun::star::uno::makeAny;
44     using ::com::sun::star::uno::Sequence;
45     using ::com::sun::star::uno::XComponentContext;
46     using ::com::sun::star::container::NoSuchElementException;
47     using ::com::sun::star::lang::IllegalArgumentException;
48     using ::com::sun::star::lang::IllegalAccessException;
49     using ::com::sun::star::container::ElementExistException;
50     using ::com::sun::star::sdb::XDatabaseRegistrations;
51     using ::com::sun::star::sdb::XDatabaseRegistrationsListener;
52     using ::com::sun::star::sdb::DatabaseRegistrationEvent;
53     using ::com::sun::star::uno::XAggregation;
54 
getConfigurationRootPath()55     static OUString getConfigurationRootPath()
56     {
57         return "org.openoffice.Office.DataAccess/RegisteredNames";
58     }
59 
getLocationNodeName()60     static OUString getLocationNodeName()
61     {
62         return "Location";
63     }
64 
getNameNodeName()65     static OUString getNameNodeName()
66     {
67         return "Name";
68     }
69 
70     // DatabaseRegistrations - declaration
71     typedef ::cppu::WeakAggImplHelper1  <   XDatabaseRegistrations
72                                         >   DatabaseRegistrations_Base;
73     class DatabaseRegistrations :public ::cppu::BaseMutex
74                                 ,public DatabaseRegistrations_Base
75     {
76     public:
77         explicit DatabaseRegistrations( const Reference<XComponentContext>& _rxContext );
78 
79     protected:
80         virtual ~DatabaseRegistrations() override;
81 
82     public:
83         virtual sal_Bool SAL_CALL hasRegisteredDatabase( const OUString& Name ) override;
84         virtual Sequence< OUString > SAL_CALL getRegistrationNames() override;
85         virtual OUString SAL_CALL getDatabaseLocation( const OUString& Name ) override;
86         virtual void SAL_CALL registerDatabaseLocation( const OUString& Name, const OUString& Location ) override;
87         virtual void SAL_CALL revokeDatabaseLocation( const OUString& Name ) override;
88         virtual void SAL_CALL changeDatabaseLocation( const OUString& Name, const OUString& NewLocation ) override;
89         virtual sal_Bool SAL_CALL isDatabaseRegistrationReadOnly( const OUString& Name ) override;
90         virtual void SAL_CALL addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) override;
91         virtual void SAL_CALL removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) override;
92 
93     private:
94         void
95                 impl_checkValidName_common(const OUString& _rName);
96         ::utl::OConfigurationNode
97                 impl_checkValidName_throw_must_exist(const OUString& _rName);
98         ::utl::OConfigurationNode
99                 impl_checkValidName_throw_must_not_exist(const OUString& _rName);
100 
101         void    impl_checkValidLocation_throw( const OUString& _rLocation );
102 
103         /** retrieves the configuration node whose "Name" sub node has the given value
104 
105             Since we separated the name of the registration node from the "Name" value of the registration, we cannot
106             simply do a "getByName" (equivalent) when we want to retrieve the node for a given registration name.
107             Instead, we must search all nodes.
108 
109             If a node with the given display name does not exist, then a NoSuchElementException is thrown.
110 
111             If no exception is thrown, then a valid node is returned: If the node existed it is returned.
112         */
113         ::utl::OConfigurationNode
114                 impl_getNodeForName_throw_must_exist(const OUString& _rName);
115 
116         /** retrieves the configuration node whose "Name" sub node has the given value
117 
118             Since we separated the name of the registration node from the "Name" value of the registration, we cannot
119             simply do a "getByName" (equivalent) when we want to retrieve the node for a given registration name.
120             Instead, we must search all nodes.
121 
122             If a node with the given name already exists, then an ElementExistException is thrown.
123 
124             If no exception is thrown, then a valid node is returned: If the node did not yet exist a new node is created,
125             in this case the root node is not yet committed.
126         */
127         ::utl::OConfigurationNode
128                 impl_getNodeForName_throw_must_not_exist(const OUString& _rName);
129 
130 
131         ::utl::OConfigurationNode
132                 impl_getNodeForName_nothrow(const OUString& _rName);
133 
134     private:
135         Reference<XComponentContext>        m_aContext;
136         ::utl::OConfigurationTreeRoot       m_aConfigurationRoot;
137         ::comphelper::OInterfaceContainerHelper2  m_aRegistrationListeners;
138     };
139 
140     // DatabaseRegistrations - implementation
DatabaseRegistrations(const Reference<XComponentContext> & _rxContext)141     DatabaseRegistrations::DatabaseRegistrations( const Reference<XComponentContext> & _rxContext )
142         :m_aContext( _rxContext )
143         ,m_aConfigurationRoot()
144         ,m_aRegistrationListeners( m_aMutex )
145     {
146         m_aConfigurationRoot = ::utl::OConfigurationTreeRoot::createWithComponentContext(
147             m_aContext, getConfigurationRootPath() );
148     }
149 
~DatabaseRegistrations()150     DatabaseRegistrations::~DatabaseRegistrations()
151     {
152     }
153 
impl_getNodeForName_nothrow(const OUString & _rName)154     ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_nothrow( const OUString& _rName )
155     {
156         const Sequence< OUString > aNames( m_aConfigurationRoot.getNodeNames() );
157         for ( auto const & nodeName : aNames )
158         {
159             ::utl::OConfigurationNode aNodeForName = m_aConfigurationRoot.openNode( nodeName );
160 
161             OUString sTestName;
162             OSL_VERIFY( aNodeForName.getNodeValue( getNameNodeName() ) >>= sTestName );
163             if ( sTestName == _rName )
164                 return aNodeForName;
165         }
166         return ::utl::OConfigurationNode();
167     }
168 
impl_getNodeForName_throw_must_exist(const OUString & _rName)169     ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_throw_must_exist(const OUString& _rName)
170     {
171         ::utl::OConfigurationNode aNodeForName( impl_getNodeForName_nothrow( _rName ) );
172 
173         if (!aNodeForName.isValid())
174         {
175             throw NoSuchElementException( _rName, *this );
176         }
177 
178         return aNodeForName;
179     }
180 
impl_getNodeForName_throw_must_not_exist(const OUString & _rName)181     ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_throw_must_not_exist(const OUString& _rName)
182     {
183         ::utl::OConfigurationNode aNodeForName( impl_getNodeForName_nothrow( _rName ) );
184 
185         if (aNodeForName.isValid())
186             throw ElementExistException( _rName, *this );
187 
188         // make unique
189         OUString sNewNodeName = "org.openoffice." + _rName;
190         while ( m_aConfigurationRoot.hasByName( sNewNodeName ) )
191         {
192             sNewNodeName = "org.openoffice." + _rName + " 2";
193         }
194 
195         ::utl::OConfigurationNode aNewNode( m_aConfigurationRoot.createNode( sNewNodeName ) );
196         aNewNode.setNodeValue( getNameNodeName(), makeAny( _rName ) );
197         return aNewNode;
198     }
199 
impl_checkValidName_common(const OUString & _rName)200     void DatabaseRegistrations::impl_checkValidName_common(const OUString& _rName)
201     {
202         if ( !m_aConfigurationRoot.isValid() )
203             throw RuntimeException( OUString(), *this );
204 
205         if ( _rName.isEmpty() )
206             throw IllegalArgumentException( OUString(), *this, 1 );
207     }
208 
impl_checkValidName_throw_must_exist(const OUString & _rName)209     ::utl::OConfigurationNode DatabaseRegistrations::impl_checkValidName_throw_must_exist(const OUString& _rName)
210     {
211         impl_checkValidName_common(_rName);
212         return impl_getNodeForName_throw_must_exist(_rName);
213     }
214 
impl_checkValidName_throw_must_not_exist(const OUString & _rName)215     ::utl::OConfigurationNode DatabaseRegistrations::impl_checkValidName_throw_must_not_exist(const OUString& _rName)
216     {
217         impl_checkValidName_common(_rName);
218         return impl_getNodeForName_throw_must_not_exist(_rName);
219     }
220 
impl_checkValidLocation_throw(const OUString & _rLocation)221     void DatabaseRegistrations::impl_checkValidLocation_throw( const OUString& _rLocation )
222     {
223         if ( _rLocation.isEmpty() )
224             throw IllegalArgumentException( OUString(), *this, 2 );
225 
226         INetURLObject aURL( _rLocation );
227         if ( aURL.GetProtocol() == INetProtocol::NotValid )
228             throw IllegalArgumentException( OUString(), *this, 2 );
229     }
230 
hasRegisteredDatabase(const OUString & Name)231     sal_Bool SAL_CALL DatabaseRegistrations::hasRegisteredDatabase( const OUString& Name )
232     {
233         ::osl::MutexGuard aGuard( m_aMutex );
234         ::utl::OConfigurationNode aNodeForName = impl_getNodeForName_nothrow( Name );
235         return aNodeForName.isValid();
236     }
237 
getRegistrationNames()238     Sequence< OUString > SAL_CALL DatabaseRegistrations::getRegistrationNames()
239     {
240         ::osl::MutexGuard aGuard( m_aMutex );
241         if ( !m_aConfigurationRoot.isValid() )
242             throw RuntimeException( OUString(), *this );
243 
244         const Sequence< OUString > aProgrammaticNames( m_aConfigurationRoot.getNodeNames() );
245         Sequence< OUString > aDisplayNames( aProgrammaticNames.getLength() );
246         OUString* pDisplayName = aDisplayNames.getArray();
247 
248         for ( auto const & name : aProgrammaticNames )
249         {
250             ::utl::OConfigurationNode aRegistrationNode = m_aConfigurationRoot.openNode( name );
251             OSL_VERIFY( aRegistrationNode.getNodeValue( getNameNodeName() ) >>= *pDisplayName );
252             ++pDisplayName;
253         }
254 
255         return aDisplayNames;
256     }
257 
getDatabaseLocation(const OUString & Name)258     OUString SAL_CALL DatabaseRegistrations::getDatabaseLocation( const OUString& Name )
259     {
260         ::osl::MutexGuard aGuard( m_aMutex );
261 
262         ::utl::OConfigurationNode aNodeForName = impl_checkValidName_throw_must_exist(Name);
263 
264         OUString sLocation;
265         OSL_VERIFY( aNodeForName.getNodeValue( getLocationNodeName() ) >>= sLocation );
266         sLocation = SvtPathOptions().SubstituteVariable( sLocation );
267 
268         return sLocation;
269     }
270 
registerDatabaseLocation(const OUString & Name,const OUString & Location)271     void SAL_CALL DatabaseRegistrations::registerDatabaseLocation( const OUString& Name, const OUString& Location )
272     {
273         ::osl::ClearableMutexGuard aGuard( m_aMutex );
274 
275         // check
276         impl_checkValidLocation_throw( Location );
277         ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_not_exist(Name);
278 
279         // register
280         aDataSourceRegistration.setNodeValue( getLocationNodeName(), makeAny( Location ) );
281         m_aConfigurationRoot.commit();
282 
283         // notify
284         DatabaseRegistrationEvent aEvent( *this, Name, OUString(), Location );
285         aGuard.clear();
286         m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::registeredDatabaseLocation, aEvent );
287     }
288 
revokeDatabaseLocation(const OUString & Name)289     void SAL_CALL DatabaseRegistrations::revokeDatabaseLocation( const OUString& Name )
290     {
291         ::osl::ClearableMutexGuard aGuard( m_aMutex );
292 
293         // check
294         ::utl::OConfigurationNode aNodeForName = impl_checkValidName_throw_must_exist(Name);
295 
296         // obtain properties for notification
297         OUString sLocation;
298         OSL_VERIFY( aNodeForName.getNodeValue( getLocationNodeName() ) >>= sLocation );
299 
300         // revoke
301         if  (   aNodeForName.isReadonly()
302             ||  !m_aConfigurationRoot.removeNode( aNodeForName.getLocalName() )
303             )
304             throw IllegalAccessException( OUString(), *this );
305 
306         m_aConfigurationRoot.commit();
307 
308         // notify
309         DatabaseRegistrationEvent aEvent( *this, Name, sLocation, OUString() );
310         aGuard.clear();
311         m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::revokedDatabaseLocation, aEvent );
312     }
313 
changeDatabaseLocation(const OUString & Name,const OUString & NewLocation)314     void SAL_CALL DatabaseRegistrations::changeDatabaseLocation( const OUString& Name, const OUString& NewLocation )
315     {
316         ::osl::ClearableMutexGuard aGuard( m_aMutex );
317 
318         // check
319         impl_checkValidLocation_throw( NewLocation );
320         ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_exist(Name);
321 
322         if  ( aDataSourceRegistration.isReadonly() )
323             throw IllegalAccessException( OUString(), *this );
324 
325         // obtain properties for notification
326         OUString sOldLocation;
327         OSL_VERIFY( aDataSourceRegistration.getNodeValue( getLocationNodeName() ) >>= sOldLocation );
328 
329         // change
330         aDataSourceRegistration.setNodeValue( getLocationNodeName(), makeAny( NewLocation ) );
331         m_aConfigurationRoot.commit();
332 
333         // notify
334         DatabaseRegistrationEvent aEvent( *this, Name, sOldLocation, NewLocation );
335         aGuard.clear();
336         m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::changedDatabaseLocation, aEvent );
337     }
338 
isDatabaseRegistrationReadOnly(const OUString & Name)339     sal_Bool SAL_CALL DatabaseRegistrations::isDatabaseRegistrationReadOnly( const OUString& Name )
340     {
341         ::osl::MutexGuard aGuard( m_aMutex );
342         ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw_must_exist(Name);
343         return aDataSourceRegistration.isReadonly();
344     }
345 
addDatabaseRegistrationsListener(const Reference<XDatabaseRegistrationsListener> & Listener)346     void SAL_CALL DatabaseRegistrations::addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener )
347     {
348         if ( Listener.is() )
349             m_aRegistrationListeners.addInterface( Listener );
350     }
351 
removeDatabaseRegistrationsListener(const Reference<XDatabaseRegistrationsListener> & Listener)352     void SAL_CALL DatabaseRegistrations::removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener )
353     {
354         if ( Listener.is() )
355             m_aRegistrationListeners.removeInterface( Listener );
356     }
357 
358     // DatabaseRegistrations - factory
createDataSourceRegistrations(const Reference<XComponentContext> & _rxContext)359     Reference< XAggregation > createDataSourceRegistrations( const Reference<XComponentContext> & _rxContext )
360     {
361         return new DatabaseRegistrations( _rxContext );
362     }
363 
364 } // namespace dbaccess
365 
366 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
367