1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_dbaccess.hxx"
26 
27 /** === begin UNO includes === **/
28 #include <com/sun/star/sdb/XDatabaseRegistrations.hpp>
29 /** === end UNO includes === **/
30 
31 #include <comphelper/componentcontext.hxx>
32 #include <cppuhelper/basemutex.hxx>
33 #include <cppuhelper/interfacecontainer.hxx>
34 #include <cppuhelper/implbase1.hxx>
35 #include <rtl/ustrbuf.hxx>
36 #include <unotools/pathoptions.hxx>
37 #include <tools/urlobj.hxx>
38 #include <unotools/confignode.hxx>
39 
40 //........................................................................
41 namespace dbaccess
42 {
43 //........................................................................
44 
45 	/** === begin UNO using === **/
46 	using ::com::sun::star::uno::Reference;
47 	using ::com::sun::star::uno::XInterface;
48 	using ::com::sun::star::uno::UNO_QUERY;
49 	using ::com::sun::star::uno::UNO_QUERY_THROW;
50 	using ::com::sun::star::uno::UNO_SET_THROW;
51 	using ::com::sun::star::uno::Exception;
52 	using ::com::sun::star::uno::RuntimeException;
53 	using ::com::sun::star::uno::Any;
54 	using ::com::sun::star::uno::makeAny;
55 	using ::com::sun::star::uno::Sequence;
56 	using ::com::sun::star::uno::Type;
57     using ::com::sun::star::container::NoSuchElementException;
58     using ::com::sun::star::lang::IllegalArgumentException;
59     using ::com::sun::star::lang::IllegalAccessException;
60     using ::com::sun::star::container::ElementExistException;
61     using ::com::sun::star::sdb::XDatabaseRegistrations;
62     using ::com::sun::star::sdb::XDatabaseRegistrationsListener;
63     using ::com::sun::star::sdb::DatabaseRegistrationEvent;
64     using ::com::sun::star::uno::XAggregation;
65 	/** === end UNO using === **/
66 
67 	//--------------------------------------------------------------------
getConfigurationRootPath()68 	static const ::rtl::OUString& getConfigurationRootPath()
69 	{
70 		static ::rtl::OUString s_sNodeName = ::rtl::OUString::createFromAscii("org.openoffice.Office.DataAccess/RegisteredNames");
71 		return s_sNodeName;
72 	}
73 
74 	//--------------------------------------------------------------------
getLocationNodeName()75     const ::rtl::OUString& getLocationNodeName()
76 	{
77 		static ::rtl::OUString s_sNodeName = ::rtl::OUString::createFromAscii( "Location" );
78 		return s_sNodeName;
79 	}
80 
81 	//--------------------------------------------------------------------
getNameNodeName()82 	const ::rtl::OUString& getNameNodeName()
83 	{
84 		static ::rtl::OUString s_sNodeName = ::rtl::OUString::createFromAscii( "Name" );
85 		return s_sNodeName;
86 	}
87 
88     //====================================================================
89 	//= DatabaseRegistrations - declaration
90 	//====================================================================
91     typedef ::cppu::WeakAggImplHelper1  <   XDatabaseRegistrations
92                                         >   DatabaseRegistrations_Base;
93     class DatabaseRegistrations :public ::cppu::BaseMutex
94                                 ,public DatabaseRegistrations_Base
95     {
96     public:
97         DatabaseRegistrations( const ::comphelper::ComponentContext& _rxContext );
98 
99     protected:
100         ~DatabaseRegistrations();
101 
102     public:
103         virtual ::sal_Bool SAL_CALL hasRegisteredDatabase( const ::rtl::OUString& _Name ) throw (IllegalArgumentException, RuntimeException);
104         virtual Sequence< ::rtl::OUString > SAL_CALL getRegistrationNames() throw (RuntimeException);
105         virtual ::rtl::OUString SAL_CALL getDatabaseLocation( const ::rtl::OUString& _Name ) throw (IllegalArgumentException, NoSuchElementException, RuntimeException);
106         virtual void SAL_CALL registerDatabaseLocation( const ::rtl::OUString& _Name, const ::rtl::OUString& _Location ) throw (IllegalArgumentException, ElementExistException, RuntimeException);
107         virtual void SAL_CALL revokeDatabaseLocation( const ::rtl::OUString& _Name ) throw (IllegalArgumentException, NoSuchElementException, IllegalAccessException, RuntimeException);
108         virtual void SAL_CALL changeDatabaseLocation( const ::rtl::OUString& Name, const ::rtl::OUString& NewLocation ) throw (IllegalArgumentException, NoSuchElementException, IllegalAccessException, RuntimeException);
109         virtual ::sal_Bool SAL_CALL isDatabaseRegistrationReadOnly( const ::rtl::OUString& _Name ) throw (IllegalArgumentException, NoSuchElementException, RuntimeException);
110         virtual void SAL_CALL addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) throw (RuntimeException);
111         virtual void SAL_CALL removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& Listener ) throw (RuntimeException);
112 
113     private:
114         ::utl::OConfigurationNode
115                 impl_checkValidName_throw( const ::rtl::OUString& _rName, const bool _bMustExist );
116 
117         void    impl_checkValidLocation_throw( const ::rtl::OUString& _rLocation );
118 
119         /** retrieves the configuration node whose "Name" sub node has the given value
120 
121             Since we separated the name of the registration node from the "Name" value of the registration, we cannot
122             simply do a "getByName" (equivalent) when we want to retrieve the node for a given registration name.
123             Instead, we must search all nodes.
124 
125             If _bMustExist is <TRUE/>, and a node with the given display name does not exist, then a NoSuchElementException
126             is thrown.
127 
128             If _bMustExist is <FALSE/>, and a node with the given name already exists, then a ElementExistException is
129             thrown.
130 
131             In either case, if no exception is thrown, then a valid node is returned: If the node existed and was allowed
132             to exist, it is returned, if the node did not yet exist, and was required to not exist, a new node is created.
133             However, in this case the root node is not yet committed.
134         */
135         ::utl::OConfigurationNode
136                 impl_getNodeForName_throw( const ::rtl::OUString& _rName, const bool _bMustExist );
137 
138         ::utl::OConfigurationNode
139                 impl_getNodeForName_nothrow( const ::rtl::OUString& _rName );
140 
141     private:
142         ::comphelper::ComponentContext      m_aContext;
143         ::utl::OConfigurationTreeRoot       m_aConfigurationRoot;
144         ::cppu::OInterfaceContainerHelper   m_aRegistrationListeners;
145     };
146 
147 	//====================================================================
148 	//= DatabaseRegistrations - implementation
149 	//====================================================================
150     //--------------------------------------------------------------------
DatabaseRegistrations(const::comphelper::ComponentContext & _rxContext)151     DatabaseRegistrations::DatabaseRegistrations( const ::comphelper::ComponentContext& _rxContext )
152         :m_aContext( _rxContext )
153         ,m_aConfigurationRoot()
154         ,m_aRegistrationListeners( m_aMutex )
155     {
156         m_aConfigurationRoot = ::utl::OConfigurationTreeRoot::createWithServiceFactory(
157             m_aContext.getLegacyServiceFactory(), getConfigurationRootPath(), -1, ::utl::OConfigurationTreeRoot::CM_UPDATABLE );
158     }
159 
160     //--------------------------------------------------------------------
~DatabaseRegistrations()161     DatabaseRegistrations::~DatabaseRegistrations()
162     {
163     }
164 
165     //--------------------------------------------------------------------
impl_getNodeForName_nothrow(const::rtl::OUString & _rName)166     ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_nothrow( const ::rtl::OUString& _rName )
167     {
168         Sequence< ::rtl::OUString > aNames( m_aConfigurationRoot.getNodeNames() );
169         for (   const ::rtl::OUString* pName = aNames.getConstArray();
170                 pName != aNames.getConstArray() + aNames.getLength();
171                 ++pName
172             )
173         {
174             ::utl::OConfigurationNode aNodeForName = m_aConfigurationRoot.openNode( *pName );
175 
176             ::rtl::OUString sTestName;
177             OSL_VERIFY( aNodeForName.getNodeValue( getNameNodeName() ) >>= sTestName );
178             if ( sTestName == _rName )
179                 return aNodeForName;
180         }
181         return ::utl::OConfigurationNode();
182     }
183 
184     //--------------------------------------------------------------------
impl_getNodeForName_throw(const::rtl::OUString & _rName,const bool _bMustExist)185     ::utl::OConfigurationNode DatabaseRegistrations::impl_getNodeForName_throw( const ::rtl::OUString& _rName, const bool _bMustExist )
186     {
187         ::utl::OConfigurationNode aNodeForName( impl_getNodeForName_nothrow( _rName ) );
188 
189         if ( aNodeForName.isValid() )
190         {
191             if ( !_bMustExist )
192                 throw ElementExistException( _rName, *this );
193 
194             return aNodeForName;
195         }
196 
197         if ( _bMustExist )
198             throw NoSuchElementException( _rName, *this );
199 
200         ::rtl::OUString sNewNodeName;
201         {
202             ::rtl::OUStringBuffer aNewNodeName;
203             aNewNodeName.appendAscii( "org.openoffice." );
204             aNewNodeName.append( _rName );
205 
206             // make unique
207             ::rtl::OUStringBuffer aReset( aNewNodeName );
208             sNewNodeName = aNewNodeName.makeStringAndClear();
209             sal_Int32 i=2;
210             while ( m_aConfigurationRoot.hasByName( sNewNodeName ) )
211             {
212                 aNewNodeName = aReset;
213                 aNewNodeName.appendAscii( " " );
214                 aNewNodeName.append( i );
215                 sNewNodeName = aNewNodeName.makeStringAndClear();
216             }
217         }
218 
219         ::utl::OConfigurationNode aNewNode( m_aConfigurationRoot.createNode( sNewNodeName ) );
220         aNewNode.setNodeValue( getNameNodeName(), makeAny( _rName ) );
221         return aNewNode;
222     }
223 
224     //--------------------------------------------------------------------
impl_checkValidName_throw(const::rtl::OUString & _rName,const bool _bMustExist)225     ::utl::OConfigurationNode DatabaseRegistrations::impl_checkValidName_throw( const ::rtl::OUString& _rName, const bool _bMustExist )
226     {
227         if ( !m_aConfigurationRoot.isValid() )
228             throw RuntimeException( ::rtl::OUString(), *this );
229 
230         if ( !_rName.getLength() )
231             throw IllegalArgumentException( ::rtl::OUString(), *this, 1 );
232 
233         return impl_getNodeForName_throw( _rName, _bMustExist );
234     }
235 
236     //--------------------------------------------------------------------
impl_checkValidLocation_throw(const::rtl::OUString & _rLocation)237     void DatabaseRegistrations::impl_checkValidLocation_throw( const ::rtl::OUString& _rLocation )
238     {
239         if ( !_rLocation.getLength() )
240             throw IllegalArgumentException( ::rtl::OUString(), *this, 2 );
241 
242         INetURLObject aURL( _rLocation );
243         if ( aURL.GetProtocol() == INET_PROT_NOT_VALID )
244             throw IllegalArgumentException( ::rtl::OUString(), *this, 2 );
245     }
246 
247     //--------------------------------------------------------------------
hasRegisteredDatabase(const::rtl::OUString & _Name)248     ::sal_Bool SAL_CALL DatabaseRegistrations::hasRegisteredDatabase( const ::rtl::OUString& _Name ) throw (IllegalArgumentException, RuntimeException)
249     {
250         ::osl::MutexGuard aGuard( m_aMutex );
251         ::utl::OConfigurationNode aNodeForName = impl_getNodeForName_nothrow( _Name );
252         return aNodeForName.isValid();
253     }
254 
255     //------------------------------------------------------------------------------
getRegistrationNames()256     Sequence< ::rtl::OUString > SAL_CALL DatabaseRegistrations::getRegistrationNames() throw (RuntimeException)
257     {
258         ::osl::MutexGuard aGuard( m_aMutex );
259         if ( !m_aConfigurationRoot.isValid() )
260             throw RuntimeException( ::rtl::OUString(), *this );
261 
262         Sequence< ::rtl::OUString > aProgrammaticNames( m_aConfigurationRoot.getNodeNames() );
263         Sequence< ::rtl::OUString > aDisplayNames( aProgrammaticNames.getLength() );
264         ::rtl::OUString* pDisplayName = aDisplayNames.getArray();
265 
266         for (   const ::rtl::OUString* pName = aProgrammaticNames.getConstArray();
267                 pName != aProgrammaticNames.getConstArray() + aProgrammaticNames.getLength();
268                 ++pName, ++pDisplayName
269             )
270         {
271             ::utl::OConfigurationNode aRegistrationNode = m_aConfigurationRoot.openNode( *pName );
272             OSL_VERIFY( aRegistrationNode.getNodeValue( getNameNodeName() ) >>= *pDisplayName );
273 
274         }
275 
276         return aDisplayNames;
277     }
278 
279     //--------------------------------------------------------------------
getDatabaseLocation(const::rtl::OUString & _Name)280     ::rtl::OUString SAL_CALL DatabaseRegistrations::getDatabaseLocation( const ::rtl::OUString& _Name ) throw (IllegalArgumentException, NoSuchElementException, RuntimeException)
281     {
282         ::osl::MutexGuard aGuard( m_aMutex );
283 
284         ::utl::OConfigurationNode aNodeForName = impl_checkValidName_throw( _Name, true );
285 
286         ::rtl::OUString sLocation;
287         OSL_VERIFY( aNodeForName.getNodeValue( getLocationNodeName() ) >>= sLocation );
288         sLocation = SvtPathOptions().SubstituteVariable( sLocation );
289 
290         return sLocation;
291     }
292 
293     //--------------------------------------------------------------------
registerDatabaseLocation(const::rtl::OUString & _Name,const::rtl::OUString & _Location)294     void SAL_CALL DatabaseRegistrations::registerDatabaseLocation( const ::rtl::OUString& _Name, const ::rtl::OUString& _Location ) throw (IllegalArgumentException, ElementExistException, RuntimeException)
295     {
296         ::osl::ClearableMutexGuard aGuard( m_aMutex );
297 
298         // check
299         impl_checkValidLocation_throw( _Location );
300         ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw( _Name, false );
301 
302         // register
303         aDataSourceRegistration.setNodeValue( getLocationNodeName(), makeAny( _Location ) );
304         m_aConfigurationRoot.commit();
305 
306         // notify
307         DatabaseRegistrationEvent aEvent( *this, _Name, ::rtl::OUString(), _Location );
308         aGuard.clear();
309         m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::registeredDatabaseLocation, aEvent );
310     }
311 
312     //--------------------------------------------------------------------
revokeDatabaseLocation(const::rtl::OUString & _Name)313     void SAL_CALL DatabaseRegistrations::revokeDatabaseLocation( const ::rtl::OUString& _Name ) throw (IllegalArgumentException, NoSuchElementException, IllegalAccessException, RuntimeException)
314     {
315         ::osl::ClearableMutexGuard aGuard( m_aMutex );
316 
317         // check
318         ::utl::OConfigurationNode aNodeForName = impl_checkValidName_throw( _Name, true );
319 
320         // obtain properties for notification
321         ::rtl::OUString sLocation;
322         OSL_VERIFY( aNodeForName.getNodeValue( getLocationNodeName() ) >>= sLocation );
323 
324         // revoke
325         if  (   aNodeForName.isReadonly()
326             ||  !m_aConfigurationRoot.removeNode( aNodeForName.getLocalName() )
327             )
328             throw IllegalAccessException( ::rtl::OUString(), *this );
329 
330         m_aConfigurationRoot.commit();
331 
332         // notify
333         DatabaseRegistrationEvent aEvent( *this, _Name, sLocation, ::rtl::OUString() );
334         aGuard.clear();
335         m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::revokedDatabaseLocation, aEvent );
336     }
337 
338     //--------------------------------------------------------------------
changeDatabaseLocation(const::rtl::OUString & _Name,const::rtl::OUString & _NewLocation)339     void SAL_CALL DatabaseRegistrations::changeDatabaseLocation( const ::rtl::OUString& _Name, const ::rtl::OUString& _NewLocation ) throw (IllegalArgumentException, NoSuchElementException, IllegalAccessException, RuntimeException)
340     {
341         ::osl::ClearableMutexGuard aGuard( m_aMutex );
342 
343         // check
344         impl_checkValidLocation_throw( _NewLocation );
345         ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw( _Name, true );
346 
347         if  ( aDataSourceRegistration.isReadonly() )
348             throw IllegalAccessException( ::rtl::OUString(), *this );
349 
350         // obtain properties for notification
351         ::rtl::OUString sOldLocation;
352         OSL_VERIFY( aDataSourceRegistration.getNodeValue( getLocationNodeName() ) >>= sOldLocation );
353 
354         // change
355         aDataSourceRegistration.setNodeValue( getLocationNodeName(), makeAny( _NewLocation ) );
356         m_aConfigurationRoot.commit();
357 
358         // notify
359         DatabaseRegistrationEvent aEvent( *this, _Name, sOldLocation, _NewLocation );
360         aGuard.clear();
361         m_aRegistrationListeners.notifyEach( &XDatabaseRegistrationsListener::changedDatabaseLocation, aEvent );
362     }
363 
364     //--------------------------------------------------------------------
isDatabaseRegistrationReadOnly(const::rtl::OUString & _Name)365     ::sal_Bool SAL_CALL DatabaseRegistrations::isDatabaseRegistrationReadOnly( const ::rtl::OUString& _Name ) throw (IllegalArgumentException, NoSuchElementException, RuntimeException)
366     {
367         ::osl::MutexGuard aGuard( m_aMutex );
368         ::utl::OConfigurationNode aDataSourceRegistration = impl_checkValidName_throw( _Name, true );
369         return aDataSourceRegistration.isReadonly();
370     }
371 
372     //--------------------------------------------------------------------
addDatabaseRegistrationsListener(const Reference<XDatabaseRegistrationsListener> & _Listener)373     void SAL_CALL DatabaseRegistrations::addDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& _Listener ) throw (RuntimeException)
374     {
375         if ( _Listener.is() )
376             m_aRegistrationListeners.addInterface( _Listener );
377     }
378 
379     //--------------------------------------------------------------------
removeDatabaseRegistrationsListener(const Reference<XDatabaseRegistrationsListener> & _Listener)380     void SAL_CALL DatabaseRegistrations::removeDatabaseRegistrationsListener( const Reference< XDatabaseRegistrationsListener >& _Listener ) throw (RuntimeException)
381     {
382         if ( _Listener.is() )
383             m_aRegistrationListeners.removeInterface( _Listener );
384     }
385 
386 	//====================================================================
387 	//= DatabaseRegistrations - factory
388 	//====================================================================
createDataSourceRegistrations(const::comphelper::ComponentContext & _rxContext)389     Reference< XAggregation > createDataSourceRegistrations( const ::comphelper::ComponentContext& _rxContext )
390     {
391         return new DatabaseRegistrations( _rxContext );
392     }
393 
394 //........................................................................
395 } // namespace dbaccess
396 //........................................................................
397