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