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