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