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 #ifndef _DBAUI_ODBC_CONFIG_HXX_
28 #include "odbcconfig.hxx"
29 #endif
30 #include <rtl/bootstrap.hxx>
31 #ifndef _RTL_USTRING_HXX_
32 #include <rtl/ustring.hxx>
33 #endif
34 #ifndef _RTL_USTRBUF_HXX_
35 #include <rtl/ustrbuf.hxx>
36 #endif
37 #ifndef _OSL_DIAGNOSE_H_
38 #include <osl/diagnose.h>
39 #endif
40 #ifndef _OSL_PROCESS_H_
41 #include <osl/process.h>
42 #endif
43 #ifndef _THREAD_HXX_
44 #include <osl/thread.hxx>
45 #endif
46 #ifndef _TOOLS_DEBUG_HXX
47 #include <tools/debug.hxx>
48 #endif
49 #ifndef _SV_SVAPP_HXX
50 #include <vcl/svapp.hxx>
51 #endif
52 
53 #ifdef HAVE_ODBC_SUPPORT
54 
55 #if defined(OS2)
56 #define ODBC_LIBRARY	"ODBC.DLL"
57 #define ODBC_UI_LIBRARY	"ODBCINST.DLL"
58 #endif
59 #if defined WNT
60 #define ODBC_LIBRARY	"ODBC32.DLL"
61 #define ODBC_UI_LIBRARY	"ODBCCP32.DLL"
62 #endif
63 #ifdef UNX
64 #ifdef MACOSX
65 #define ODBC_LIBRARY		"libiodbc.dylib"
66 #define ODBC_UI_LIBRARY		"libiodbcinst.dylib"
67 #else
68 #define ODBC_LIBRARY_1		"libodbc.so.1"
69 #define ODBC_UI_LIBRARY_1	"libodbcinst.so.1"
70 #define ODBC_LIBRARY		"libodbc.so"
71 #define ODBC_UI_LIBRARY		"libodbcinst.so"
72 #endif
73 #endif
74 
75 // just to go with calling convention of windows
76 // so don't touch this
77 #if defined(WNT)
78 #define SQL_API __stdcall
79 // At least under some circumstances, the below #include <odbc/sqlext.h> re-
80 // defines SQL_API to an empty string, leading to a compiler warning on MSC; to
81 // not break the current behavior, this is worked around by locally disabling
82 // that warning:
83 #if defined _MSC_VER
84 #pragma warning(push)
85 #pragma warning(disable: 4005)
86 #endif
87 #endif // defined(WNT)
88 
89 #if defined(OS2)
90 #define ALLREADY_HAVE_OS2_TYPES
91 #define DONT_TD_VOID
92 #endif
93 
94 #ifdef SYSTEM_ODBC_HEADERS
95 #include <sqlext.h>
96 #else
97 #ifndef __SQLEXT_H
98 #include <odbc/sqlext.h>
99 #endif
100 #endif
101 
102 #if defined(WNT)
103 #if defined _MSC_VER
104 #pragma warning(pop)
105 #endif
106 #undef SQL_API
107 #define SQL_API __stdcall
108 #endif // defined(WNT)
109 // from here on you can do what you want to
110 
111 #if defined(OS2)
112 #define SQL_API _System
113 #endif // defined(OS2)
114 
115 #else
116 
117 #define ODBC_LIBRARY	""
118 #define ODBC_UI_LIBRARY	""
119 
120 #endif	// HAVE_ODBC_SUPPORT
121 
122 //.........................................................................
123 namespace dbaui
124 {
125 //.........................................................................
126 
127 
128 #ifdef HAVE_ODBC_SUPPORT
129 typedef SQLRETURN (SQL_API* TSQLManageDataSource) (SQLHWND hwndParent);
130 typedef SQLRETURN (SQL_API* TSQLAllocHandle) (SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE*	OutputHandlePtr);
131 typedef SQLRETURN (SQL_API* TSQLFreeHandle) (SQLSMALLINT HandleType, SQLHANDLE Handle);
132 typedef SQLRETURN (SQL_API* TSQLSetEnvAttr) (SQLHENV EnvironmentHandle, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength);
133 typedef SQLRETURN (SQL_API* TSQLDataSources) (SQLHENV EnvironmentHandle, SQLUSMALLINT	Direction, SQLCHAR* ServerName,
134 								SQLSMALLINT BufferLength1, SQLSMALLINT* NameLength1Ptr, SQLCHAR* Description, SQLSMALLINT BufferLength2, SQLSMALLINT* NameLength2Ptr);
135 
136 #define NSQLManageDataSource(a) (*(TSQLManageDataSource)(m_pSQLManageDataSource))(a)
137 #define NSQLAllocHandle(a,b,c) (*(TSQLAllocHandle)(m_pAllocHandle))(a,b,c)
138 #define NSQLFreeHandle(a,b) (*(TSQLFreeHandle)(m_pFreeHandle))(a,b)
139 #define NSQLSetEnvAttr(a,b,c,d) (*(TSQLSetEnvAttr)(m_pSetEnvAttr))(a,b,c,d)
140 #define NSQLDataSources(a,b,c,d,e,f,g,h) (*(TSQLDataSources)(m_pDataSources))(a,b,c,d,e,f,g,h)
141 #endif
142 
143 //=========================================================================
144 //= OOdbcLibWrapper
145 //=========================================================================
146 DBG_NAME(OOdbcLibWrapper)
147 //-------------------------------------------------------------------------
148 #ifdef HAVE_ODBC_SUPPORT
149 OOdbcLibWrapper::OOdbcLibWrapper()
150 	:m_pOdbcLib(NULL)
151 {
152     DBG_CTOR(OOdbcLibWrapper,NULL);
153 
154 }
155 #endif
156 
157 //-------------------------------------------------------------------------
158 sal_Bool OOdbcLibWrapper::load(const sal_Char* _pLibPath)
159 {
160 	m_sLibPath = ::rtl::OUString::createFromAscii(_pLibPath);
161 #ifdef HAVE_ODBC_SUPPORT
162 	// load the module
163 	m_pOdbcLib = osl_loadModule(m_sLibPath.pData, SAL_LOADMODULE_NOW);
164 	return (NULL != m_pOdbcLib);
165 #endif
166 }
167 
168 //-------------------------------------------------------------------------
169 void OOdbcLibWrapper::unload()
170 {
171 #ifdef HAVE_ODBC_SUPPORT
172 	if (isLoaded())
173 	{
174 		osl_unloadModule(m_pOdbcLib);
175 		m_pOdbcLib = NULL;
176 	}
177 #endif
178 }
179 
180 //-------------------------------------------------------------------------
181 oslGenericFunction OOdbcLibWrapper::loadSymbol(const sal_Char* _pFunctionName)
182 {
183 	return osl_getFunctionSymbol(m_pOdbcLib, ::rtl::OUString::createFromAscii(_pFunctionName).pData);
184 }
185 
186 //-------------------------------------------------------------------------
187 OOdbcLibWrapper::~OOdbcLibWrapper()
188 {
189 	unload();
190 
191     DBG_DTOR(OOdbcLibWrapper,NULL);
192 }
193 
194 //=========================================================================
195 //= OOdbcEnumeration
196 //=========================================================================
197 struct OdbcTypesImpl
198 {
199 #ifdef HAVE_ODBC_SUPPORT
200 	SQLHANDLE	hEnvironment;
201 	OdbcTypesImpl() : hEnvironment(0) { }
202 #else
203 	void*		pDummy;
204 #endif
205 };
206 DBG_NAME(OOdbcEnumeration)
207 //-------------------------------------------------------------------------
208 OOdbcEnumeration::OOdbcEnumeration()
209 #ifdef HAVE_ODBC_SUPPORT
210 	:m_pAllocHandle(NULL)
211 	,m_pSetEnvAttr(NULL)
212 	,m_pDataSources(NULL)
213 	,m_pImpl(new OdbcTypesImpl)
214 #endif
215 {
216     DBG_CTOR(OOdbcEnumeration,NULL);
217 
218 	sal_Bool bLoaded = load(ODBC_LIBRARY);
219 #ifdef ODBC_LIBRARY_1
220 	if ( !bLoaded )
221 		bLoaded = load(ODBC_LIBRARY_1);
222 #endif
223 
224 	if ( bLoaded )
225 	{
226 #ifdef HAVE_ODBC_SUPPORT
227 		// load the generic functions
228 		m_pAllocHandle = loadSymbol("SQLAllocHandle");
229 		m_pFreeHandle = loadSymbol("SQLFreeHandle");
230 		m_pSetEnvAttr = loadSymbol("SQLSetEnvAttr");
231 		m_pDataSources = loadSymbol("SQLDataSources");
232 
233 		// all or nothing
234 		if (!m_pAllocHandle || !m_pSetEnvAttr || !m_pDataSources || !m_pFreeHandle)
235 		{
236 			unload();
237 			m_pAllocHandle = m_pFreeHandle = m_pSetEnvAttr = m_pDataSources = NULL;
238 		}
239 #endif
240 	}
241 }
242 
243 //-------------------------------------------------------------------------
244 OOdbcEnumeration::~OOdbcEnumeration()
245 {
246 	freeEnv();
247 	delete m_pImpl;
248 
249     DBG_DTOR(OOdbcEnumeration,NULL);
250 }
251 
252 //-------------------------------------------------------------------------
253 sal_Bool OOdbcEnumeration::allocEnv()
254 {
255 	OSL_ENSURE(isLoaded(), "OOdbcEnumeration::allocEnv: not loaded!");
256 	if (!isLoaded())
257 		return sal_False;
258 
259 #ifdef HAVE_ODBC_SUPPORT
260 	if (m_pImpl->hEnvironment)
261 		// nothing to do
262 		return sal_True;
263 	SQLRETURN nResult = NSQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_pImpl->hEnvironment);
264 	if (SQL_SUCCESS != nResult)
265 		// can't do anything without environment
266 		return sal_False;
267 
268 	NSQLSetEnvAttr(m_pImpl->hEnvironment, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
269 	return sal_True;
270 #else
271 	return sal_False;
272 #endif
273 }
274 
275 //-------------------------------------------------------------------------
276 void OOdbcEnumeration::freeEnv()
277 {
278 #ifdef HAVE_ODBC_SUPPORT
279 	if (m_pImpl->hEnvironment)
280 		NSQLFreeHandle(SQL_HANDLE_ENV, m_pImpl->hEnvironment);
281 	m_pImpl->hEnvironment  = 0;
282 #endif
283 }
284 
285 //-------------------------------------------------------------------------
286 void OOdbcEnumeration::getDatasourceNames(StringBag& _rNames)
287 {
288 	OSL_ENSURE(isLoaded(), "OOdbcEnumeration::getDatasourceNames: not loaded!");
289 	if (!isLoaded())
290 		return;
291 
292 	if (!allocEnv())
293 	{
294 		OSL_ENSURE(sal_False, "OOdbcEnumeration::getDatasourceNames: could not allocate an ODBC environment!");
295 		return;
296 	}
297 
298 #ifdef HAVE_ODBC_SUPPORT
299 	// now that we have an environment collect the data source names
300 	UCHAR szDSN[SQL_MAX_DSN_LENGTH+1];
301 	SWORD pcbDSN;
302 	UCHAR szDescription[1024+1];
303 	SWORD pcbDescription;
304 	SQLRETURN nResult = SQL_SUCCESS;
305     rtl_TextEncoding nTextEncoding = osl_getThreadTextEncoding();
306 
307 	for (	nResult = NSQLDataSources(m_pImpl->hEnvironment, SQL_FETCH_FIRST, szDSN, sizeof(szDSN), &pcbDSN, szDescription, sizeof(szDescription)-1, &pcbDescription);
308 			;
309 			nResult = NSQLDataSources(m_pImpl->hEnvironment, SQL_FETCH_NEXT, szDSN, sizeof(szDSN), &pcbDSN, szDescription, sizeof(szDescription)-1, &pcbDescription)
310 		)
311 	{
312 		if (nResult != SQL_SUCCESS)
313 			// no further error handling
314 			break;
315 		else
316 		{
317             ::rtl::OUString aCurrentDsn(reinterpret_cast<const char*>(szDSN),pcbDSN, nTextEncoding);
318 			_rNames.insert(aCurrentDsn);
319 		}
320 	}
321 #endif
322 }
323 
324 #ifdef HAVE_ODBC_ADMINISTRATION
325 
326 //=========================================================================
327 //= ProcessTerminationWait
328 //=========================================================================
329 class ProcessTerminationWait : public ::osl::Thread
330 {
331     oslProcess  m_hProcessHandle;
332     Link        m_aFinishHdl;
333 
334 public:
335     ProcessTerminationWait( oslProcess _hProcessHandle, const Link& _rFinishHdl )
336         :m_hProcessHandle( _hProcessHandle )
337         ,m_aFinishHdl( _rFinishHdl )
338     {
339     }
340 
341 protected:
342     virtual void SAL_CALL run()
343     {
344         osl_joinProcess( m_hProcessHandle );
345         osl_freeProcessHandle( m_hProcessHandle );
346         Application::PostUserEvent( m_aFinishHdl );
347     }
348 };
349 
350 //=========================================================================
351 //= OOdbcManagement
352 //=========================================================================
353 //-------------------------------------------------------------------------
354 OOdbcManagement::OOdbcManagement( const Link& _rAsyncFinishCallback )
355     :m_pProcessWait( NULL )
356     ,m_aAsyncFinishCallback( _rAsyncFinishCallback )
357 {
358 }
359 
360 //-------------------------------------------------------------------------
361 OOdbcManagement::~OOdbcManagement()
362 {
363     // wait for our thread to be finished
364     if ( m_pProcessWait.get() )
365         m_pProcessWait->join();
366 }
367 
368 //-------------------------------------------------------------------------
369 bool OOdbcManagement::manageDataSources_async()
370 {
371     OSL_PRECOND( !isRunning(), "OOdbcManagement::manageDataSources_async: still running from the previous call!" );
372     if ( isRunning() )
373         return false;
374 
375     // this is done in an external process, due to #i78733#
376     // (and note this whole functionality is supported on Windows only, ATM)
377     ::rtl::OUString sExecutableName( RTL_CONSTASCII_USTRINGPARAM( "$OOO_BASE_DIR/program/odbcconfig.exe" ) );
378     ::rtl::Bootstrap::expandMacros( sExecutableName ); //TODO: detect failure
379     oslProcess hProcessHandle(0);
380     oslProcessError eError = osl_executeProcess( sExecutableName.pData, NULL, 0, 0, NULL, NULL, NULL, 0, &hProcessHandle );
381     if ( eError != osl_Process_E_None )
382         return false;
383 
384     m_pProcessWait.reset( new ProcessTerminationWait( hProcessHandle, m_aAsyncFinishCallback ) );
385     m_pProcessWait->create();
386     return true;
387 }
388 
389 //-------------------------------------------------------------------------
390 bool OOdbcManagement::isRunning() const
391 {
392     return ( m_pProcessWait.get() && m_pProcessWait->isRunning() );
393 }
394 
395 #endif // HAVE_ODBC_ADMINISTRATION
396 
397 //.........................................................................
398 }	// namespace dbaui
399 //.........................................................................
400