Include files used to build the server are now <freeradius-devel/*.h>
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_unixodbc / sql_unixodbc.c
1 /*
2  * sql_unixodbc.c       unixODBC rlm_sql driver
3  *
4  *   This program is free software; you can redistribute it and/or modify
5  *   it under the terms of the GNU General Public License as published by
6  *   the Free Software Foundation; either version 2 of the License, or
7  *   (at your option) any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU General Public License for more details.
13  *
14  *   You should have received a copy of the GNU General Public License
15  *   along with this program; if not, write to the Free Software
16  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * Copyright 2000  The FreeRADIUS server project
19  * Copyright 2000  Dmitri Ageev <d_ageev@ortcc.ru>
20  */
21
22 #include <freeradius-devel/autoconf.h>
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <freeradius-devel/radiusd.h>
27
28 #include <sqltypes.h>
29 #include "rlm_sql.h"
30
31 typedef struct rlm_sql_unixodbc_sock {
32         SQLHENV env_handle;
33         SQLHDBC dbc_handle;
34         SQLHSTMT stmt_handle;
35         SQL_ROW row;
36         void *conn;
37 } rlm_sql_unixodbc_sock;
38
39
40 #include <sql.h>
41 #include <sqlext.h>
42
43 /* Forward declarations */
44 static char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config);
45 static int sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config);
46 static int sql_affected_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config);
47 static int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config);
48
49
50 /*************************************************************************
51  *
52  *      Function: sql_init_socket
53  *
54  *      Purpose: Establish connection to the db
55  *
56  *************************************************************************/
57 static int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
58     rlm_sql_unixodbc_sock *unixodbc_sock;
59     long err_handle;
60
61         if (!sqlsocket->conn) {
62                 sqlsocket->conn = (rlm_sql_unixodbc_sock *)rad_malloc(sizeof(rlm_sql_unixodbc_sock));
63                 if (!sqlsocket->conn) {
64                         return -1;
65                 }
66         }
67         unixodbc_sock = sqlsocket->conn;
68         memset(unixodbc_sock, 0, sizeof(*unixodbc_sock));
69
70     /* 1. Allocate environment handle and register version */
71     err_handle = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&unixodbc_sock->env_handle);
72     if (!SQL_SUCCEEDED(err_handle))
73     {
74         radlog(L_ERR, "rlm_sql_unixodbc: Can't allocate environment handle %s\n", sql_error(sqlsocket, config));
75         return -1;
76     }
77     err_handle = SQLSetEnvAttr(unixodbc_sock->env_handle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
78     if (!SQL_SUCCEEDED(err_handle))
79     {
80         radlog(L_ERR, "rlm_sql_unixodbc: Can't register ODBC version %s\n", sql_error(sqlsocket, config));
81         SQLFreeHandle(SQL_HANDLE_ENV, unixodbc_sock->env_handle);
82         return -1;
83     }
84     /* 2. Allocate connection handle */
85     err_handle = SQLAllocHandle(SQL_HANDLE_DBC, unixodbc_sock->env_handle, &unixodbc_sock->dbc_handle);
86     if (!SQL_SUCCEEDED(err_handle))
87     {
88         radlog(L_ERR, "rlm_sql_unixodbc: Can't allocate connection handle %s\n", sql_error(sqlsocket, config));
89         SQLFreeHandle(SQL_HANDLE_ENV, unixodbc_sock->env_handle);
90         return -1;
91     }
92
93     /* 3. Connect to the datasource */
94     err_handle = SQLConnect(unixodbc_sock->dbc_handle,
95         (SQLCHAR*) config->sql_server, strlen(config->sql_server),
96         (SQLCHAR*) config->sql_login, strlen(config->sql_login),
97         (SQLCHAR*) config->sql_password, strlen(config->sql_password));
98     if (!SQL_SUCCEEDED(err_handle))
99     {
100         radlog(L_ERR, "rlm_sql_unixodbc: Connection failed %s\n", sql_error(sqlsocket, config));
101         SQLFreeHandle(SQL_HANDLE_DBC, unixodbc_sock->dbc_handle);
102         SQLFreeHandle(SQL_HANDLE_ENV, unixodbc_sock->env_handle);
103         return -1;
104     }
105
106     /* 4. Allocate the statement */
107     err_handle = SQLAllocStmt(unixodbc_sock->dbc_handle, &unixodbc_sock->stmt_handle);
108     if (!SQL_SUCCEEDED(err_handle))
109     {
110         radlog(L_ERR, "rlm_sql_unixodbc: Can't allocate the statement %s\n", sql_error(sqlsocket, config));
111         return -1;
112     }
113
114     return 0;
115 }
116
117
118 /*************************************************************************
119  *
120  *      Function: sql_destroy_socket
121  *
122  *      Purpose: Free socket and private connection data
123  *
124  *************************************************************************/
125 static int sql_destroy_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config)
126 {
127         free(sqlsocket->conn);
128         sqlsocket->conn = NULL;
129         return 0;
130 }
131
132
133 /*************************************************************************
134  *
135  *      Function: sql_query
136  *
137  *      Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
138  *               the database.
139  *
140  *************************************************************************/
141 static int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
142     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
143     long err_handle;
144
145     if (config->sqltrace)
146         radlog(L_DBG, "query:  %s", querystr);
147
148     /* Executing query */
149     err_handle = SQLExecDirect(unixodbc_sock->stmt_handle, (SQLCHAR *)querystr, strlen(querystr));
150     if (!SQL_SUCCEEDED(err_handle))
151     {
152         radlog(L_ERR, "rlm_sql_unixodbc: '%s'\n", sql_error(sqlsocket, config));
153         return -1;
154     }
155     return 0;
156 }
157
158
159 /*************************************************************************
160  *
161  *      Function: sql_select_query
162  *
163  *      Purpose: Issue a select query to the database
164  *
165  *************************************************************************/
166 static int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
167     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
168     SQLINTEGER column, len;
169     int numfields;
170
171     if(sql_query(sqlsocket, config, querystr) < 0)
172         return -1;
173
174     numfields=sql_num_fields(sqlsocket, config);
175     if(numfields < 0)
176         return -1;
177
178     /* Reserving memory for result */
179     unixodbc_sock->row = (char **) rad_malloc((numfields+1)*sizeof(char *));
180     unixodbc_sock->row[numfields] = NULL;
181
182     for(column=1; column<=numfields; column++) {
183         SQLColAttributes(unixodbc_sock->stmt_handle,((SQLUSMALLINT) column),SQL_COLUMN_LENGTH,NULL,0,NULL,&len);
184         unixodbc_sock->row[column-1] = (SQLCHAR*)rad_malloc((int)++len);
185         SQLBindCol(unixodbc_sock->stmt_handle, column, SQL_C_CHAR, (SQLCHAR *)unixodbc_sock->row[column-1], len, NULL);
186     }
187         return 0;
188 }
189
190
191 /*************************************************************************
192  *
193  *      Function: sql_store_result
194  *
195  *      Purpose: database specific store_result function. Returns a result
196  *               set for the query.
197  *
198  *************************************************************************/
199 static int sql_store_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
200   /* Not used */
201     return 0;
202 }
203
204
205 /*************************************************************************
206  *
207  *      Function: sql_num_fields
208  *
209  *      Purpose: database specific num_fields function. Returns number
210  *               of columns from query
211  *
212  *************************************************************************/
213 static int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
214     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
215     long err_handle;
216     int num_fields = 0;
217
218     err_handle = SQLNumResultCols(unixodbc_sock->stmt_handle,(SQLSMALLINT *)&num_fields);
219     if (!SQL_SUCCEEDED(err_handle))
220     {
221         radlog(L_ERR, "rlm_sql_unixodbc: '%s'\n", sql_error(sqlsocket, config));
222         return -1;
223     }
224     return num_fields;
225 }
226
227
228 /*************************************************************************
229  *
230  *      Function: sql_num_rows
231  *
232  *      Purpose: database specific num_rows. Returns number of rows in
233  *               query
234  *
235  *************************************************************************/
236 static int sql_num_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
237     return sql_affected_rows(sqlsocket, config);
238 }
239
240
241 /*************************************************************************
242  *
243  *      Function: sql_fetch_row
244  *
245  *      Purpose: database specific fetch_row. Returns a SQL_ROW struct
246  *               with all the data for the query in 'sqlsocket->row'. Returns
247  *               0 on success, -1 on failure, SQL_DOWN if 'database is down'.
248  *
249  *************************************************************************/
250 static int sql_fetch_row(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
251     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
252
253     sqlsocket->row = NULL;
254
255     if(SQLFetch(unixodbc_sock->stmt_handle) == SQL_NO_DATA_FOUND)
256         return 0;
257
258     /* XXX Check if return suggests we should return error or SQL_DOWN */
259
260     sqlsocket->row = unixodbc_sock->row;
261     return 0;
262 }
263
264
265 /*************************************************************************
266  *
267  *      Function: sql_finish_select_query
268  *
269  *      Purpose: End the select query, such as freeing memory or result
270  *
271  *************************************************************************/
272 static int sql_finish_select_query(SQLSOCK * sqlsocket, SQL_CONFIG *config) {
273     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
274
275     sql_free_result(sqlsocket, config);
276     SQLFreeStmt(unixodbc_sock->stmt_handle, SQL_CLOSE);
277     return 0;
278 }
279
280 /*************************************************************************
281  *
282  *      Function: sql_finish_query
283  *
284  *      Purpose: End the query, such as freeing memory
285  *
286  *************************************************************************/
287 static int sql_finish_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
288     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
289
290     SQLFreeStmt(unixodbc_sock->stmt_handle, SQL_CLOSE);
291     return 0;
292 }
293
294 /*************************************************************************
295  *
296  *      Function: sql_free_result
297  *
298  *      Purpose: database specific free_result. Frees memory allocated
299  *               for a result set
300  *
301  *************************************************************************/
302 static int sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
303     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
304     int column, numfileds=sql_num_fields(sqlsocket, config);
305
306     /* Freeing reserved memory */
307     if(unixodbc_sock->row != NULL) {
308         for(column=0; column<numfileds; column++) {
309             if(unixodbc_sock->row[column] != NULL) {
310                 free(unixodbc_sock->row[column]);
311                 unixodbc_sock->row[column] = NULL;
312             }
313         }
314         free(unixodbc_sock->row);
315         unixodbc_sock->row = NULL;
316     }
317     return 0;
318 }
319
320 /*************************************************************************
321  *
322  *      Function: sql_close
323  *
324  *      Purpose: database specific close. Closes an open database
325  *               connection and cleans up any open handles.
326  *
327  *************************************************************************/
328 static int sql_close(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
329     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
330
331     SQLFreeStmt(unixodbc_sock->stmt_handle, SQL_DROP);
332     SQLDisconnect(unixodbc_sock->dbc_handle);
333     SQLFreeConnect(unixodbc_sock->dbc_handle);
334     SQLFreeEnv(unixodbc_sock->env_handle);
335
336     return 0;
337 }
338
339 /*************************************************************************
340  *
341  *      Function: sql_error
342  *
343  *      Purpose: database specific error. Returns error associated with
344  *               connection
345  *
346  *************************************************************************/
347 static char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
348     SQLCHAR state[256] = "";
349     SQLCHAR error[256] = "";
350     SQLINTEGER errornum = 0;
351     SQLSMALLINT length = 255;
352     static char result[1024];   /* NOT thread-safe! */
353
354     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
355
356     SQLError(
357         unixodbc_sock->env_handle,
358         unixodbc_sock->dbc_handle,
359         unixodbc_sock->stmt_handle,
360         state,
361         &errornum,
362         error,
363         256,
364         &length);
365
366     sprintf(result, "%s %s", state, error);
367     result[sizeof(result) - 1] = '\0'; /* catch idiot thread issues */
368     return result;
369 }
370
371 /*************************************************************************
372  *
373  *      Function: sql_affected_rows
374  *
375  *      Purpose: Return the number of rows affected by the query (update,
376  *               or insert)
377  *
378  *************************************************************************/
379 static int sql_affected_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
380     rlm_sql_unixodbc_sock *unixodbc_sock = sqlsocket->conn;
381     long err_handle;
382     int affected_rows;
383
384     err_handle = SQLRowCount(unixodbc_sock->stmt_handle, (SQLINTEGER *)&affected_rows);
385     if (!SQL_SUCCEEDED(err_handle))
386     {
387         radlog(L_ERR, "rlm_sql_unixodbc: '%s'\n", sql_error(sqlsocket, config));
388         return -1;
389     }
390     return affected_rows;
391 }
392
393
394 /* Exported to rlm_sql */
395 rlm_sql_module_t rlm_sql_unixodbc = {
396         "rlm_sql_unixodbc",
397         sql_init_socket,
398         sql_destroy_socket,
399         sql_query,
400         sql_select_query,
401         sql_store_result,
402         sql_num_fields,
403         sql_num_rows,
404         sql_fetch_row,
405         sql_free_result,
406         sql_error,
407         sql_close,
408         sql_finish_query,
409         sql_finish_select_query,
410         sql_affected_rows
411 };