Replace spaces with tabs
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_iodbc / rlm_sql_iodbc.c
1 /*
2  * sql_iodbc.c  iODBC support for FreeRadius
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Jeff Carneal <jeff@apex.net>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28
29 #include <sys/stat.h>
30
31 #include <isql.h>
32 #include <isqlext.h>
33 #include <sqltypes.h>
34
35 #include "rlm_sql.h"
36
37 typedef struct rlm_sql_iodbc_conn {
38         HENV    env_handle;
39         HDBC    dbc_handle;
40         HSTMT   stmt_handle;
41         int     id;
42         
43         rlm_sql_row_t row;
44
45         struct sql_socket *next;
46
47         void    *conn;
48 } rlm_sql_iodbc_conn_t;
49
50 static const char *sql_error(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
51 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
52
53 static int sql_socket_destructor(void *c)
54 {
55         rlm_sql_iodbc_conn_t *conn = c;
56         
57         DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
58         
59         if (conn->stmt_handle) {
60                 SQLFreeStmt(conn->stmt_handle, SQL_DROP);
61         }
62         
63         if (conn->dbc_handle) {
64                 SQLDisconnect(conn->dbc_handle);
65                 SQLFreeConnect(conn->dbc_handle);
66         }
67         
68         if (conn->env_handle) {
69                 SQLFreeEnv(conn->env_handle);
70         }
71         
72         return 0;
73 }
74
75 /*************************************************************************
76  *
77  *      Function: sql_socket_init
78  *
79  *      Purpose: Establish connection to the db
80  *
81  *************************************************************************/
82 static int sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
83
84         rlm_sql_iodbc_conn_t *conn;
85         SQLRETURN rcode;
86
87         MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
88         talloc_set_destructor((void *) conn, sql_socket_destructor);
89
90         rcode = SQLAllocEnv(&conn->env_handle);
91         if (!SQL_SUCCEEDED(rcode)) {
92                 radlog(L_ERR, "sql_create_socket: SQLAllocEnv failed:  %s",
93                                 sql_error(handle, config));
94                 return -1;
95         }
96
97         rcode = SQLAllocConnect(conn->env_handle,
98                                 &conn->dbc_handle);
99         if (!SQL_SUCCEEDED(rcode)) {
100                 radlog(L_ERR, "sql_create_socket: SQLAllocConnect failed:  %s",
101                                 sql_error(handle, config));
102                 return -1;
103         }
104
105         rcode = SQLConnect(conn->dbc_handle, config->sql_server,
106                            SQL_NTS, config->sql_login, SQL_NTS,
107                            config->sql_password, SQL_NTS);
108         if (!SQL_SUCCEEDED(rcode)) {
109                 radlog(L_ERR, "sql_create_socket: SQLConnectfailed:  %s",
110                                 sql_error(handle, config));
111                 return -1;
112         }
113
114         return 0;
115 }
116
117 /*************************************************************************
118  *
119  *      Function: sql_query
120  *
121  *      Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
122  *             the database.
123  *
124  *************************************************************************/
125 static int sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char *querystr) {
126
127         rlm_sql_iodbc_conn_t *conn = handle->conn;
128         SQLRETURN rcode;
129
130         rcode = SQLAllocStmt(conn->dbc_handle,
131                              &conn->stmt_handle);
132         if (!SQL_SUCCEEDED(rcode)) {
133                 radlog(L_ERR, "sql_create_socket: SQLAllocStmt failed:  %s",
134                                 sql_error(handle, config));
135                 return -1;
136         }
137
138         if (conn->dbc_handle == NULL) {
139                 radlog(L_ERR, "sql_query:  Socket not connected");
140                 return -1;
141         }
142
143         rcode = SQLExecDirect(conn->stmt_handle, querystr, SQL_NTS);
144         if (!SQL_SUCCEEDED(rcode)) {
145                 radlog(L_ERR, "sql_query: failed:  %s",
146                                 sql_error(handle, config));
147                 return -1;
148         }
149
150         return 0;
151 }
152
153
154 /*************************************************************************
155  *
156  *      Function: sql_select_query
157  *
158  *      Purpose: Issue a select query to the database
159  *
160  *************************************************************************/
161 static int sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char *querystr) {
162
163         int numfields = 0;
164         int i=0;
165         char **row=NULL;
166         SQLINTEGER len=0;
167         rlm_sql_iodbc_conn_t *conn = handle->conn;
168
169         if(sql_query(handle, config, querystr) < 0) {
170                 return -1;
171         }
172
173         numfields = sql_num_fields(handle, config);
174
175         row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
176         memset(row, 0, (sizeof(char *) * (numfields)));
177         row[numfields] = NULL;
178
179         for(i=1; i<=numfields; i++) {
180                 SQLColAttributes(conn->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH,
181                                                                                 NULL, 0, NULL, &len);
182                 len++;
183
184                 /*
185                  * Allocate space for each column
186                  */
187                 row[i-1] = (SQLCHAR*)rad_malloc((int)len);
188
189                 /*
190                  * This makes me feel dirty, but, according to Microsoft, it works.
191                  * Any ODBC datatype can be converted to a 'char *' according to
192                  * the following:
193                  *
194                  * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
195                  */
196                 SQLBindCol(conn->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
197         }
198
199         conn->row = row;
200
201         return 0;
202 }
203
204
205 /*************************************************************************
206  *
207  *      Function: sql_store_result
208  *
209  *      Purpose: database specific store_result function. Returns a result
210  *             set for the query.
211  *
212  *************************************************************************/
213 static int sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
214
215         return 0;
216 }
217
218
219 /*************************************************************************
220  *
221  *      Function: sql_num_fields
222  *
223  *      Purpose: database specific num_fields function. Returns number
224  *             of columns from query
225  *
226  *************************************************************************/
227 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
228
229         SQLSMALLINT count=0;
230         rlm_sql_iodbc_conn_t *conn = handle->conn;
231
232         SQLNumResultCols(conn->stmt_handle, &count);
233
234         return (int)count;
235 }
236
237 /*************************************************************************
238  *
239  *      Function: sql_num_rows
240  *
241  *      Purpose: database specific num_rows. Returns number of rows in
242  *             query
243  *
244  *************************************************************************/
245 static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
246         /*
247          * I presume this function is used to determine the number of
248          * rows in a result set *before* fetching them.  I don't think
249          * this is possible in ODBC 2.x, but I'd be happy to be proven
250          * wrong.  If you know how to do this, email me at jeff@apex.net
251          */
252         return 0;
253 }
254
255
256 /*************************************************************************
257  *
258  *      Function: sql_fetch_row
259  *
260  *      Purpose: database specific fetch_row. Returns a rlm_sql_row_t struct
261  *             with all the data for the query in 'handle->row'. Returns
262  *               0 on success, -1 on failure, SQL_DOWN if 'database is down'
263  *
264  *************************************************************************/
265 static int sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
266
267         SQLRETURN rc;
268         rlm_sql_iodbc_conn_t *conn = handle->conn;
269
270         handle->row = NULL;
271
272         if((rc = SQLFetch(conn->stmt_handle)) == SQL_NO_DATA_FOUND) {
273                 return 0;
274         }
275         /* XXX Check rc for database down, if so, return SQL_DOWN */
276
277         handle->row = conn->row;
278         return 0;
279 }
280
281
282
283 /*************************************************************************
284  *
285  *      Function: sql_free_result
286  *
287  *      Purpose: database specific free_result. Frees memory allocated
288  *             for a result set
289  *
290  *************************************************************************/
291 static int sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
292
293         int i=0;
294         rlm_sql_iodbc_conn_t *conn = handle->conn;
295
296         for(i=0; i<sql_num_fields(handle, config); i++) {
297                 free(conn->row[i]);
298         }
299         free(conn->row);
300         conn->row=NULL;
301
302         SQLFreeStmt( conn->stmt_handle, SQL_DROP );
303
304         return 0;
305 }
306
307
308 /*************************************************************************
309  *
310  *      Function: sql_error
311  *
312  *      Purpose: database specific error. Returns error associated with
313  *             connection
314  *
315  *************************************************************************/
316 static const char *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
317
318         SQLINTEGER errornum = 0;
319         SQLSMALLINT length = 0;
320         SQLCHAR state[256] = "";
321         static SQLCHAR error[256] = "";
322         rlm_sql_iodbc_conn_t *conn = handle->conn;
323
324         SQLError(conn->env_handle, conn->dbc_handle, conn->stmt_handle,
325                 state, &errornum, error, 256, &length);
326         return error;
327 }
328
329 /*************************************************************************
330  *
331  *      Function: sql_finish_query
332  *
333  *      Purpose: End the query, such as freeing memory
334  *
335  *************************************************************************/
336 static int sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
337
338         return sql_free_result(handle, config);
339 }
340
341 /*************************************************************************
342  *
343  *      Function: sql_finish_select_query
344  *
345  *      Purpose: End the select query, such as freeing memory or result
346  *
347  *************************************************************************/
348 static int sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
349         return sql_free_result(handle, config);
350 }
351
352 /*************************************************************************
353  *
354  *      Function: sql_affected_rows
355  *
356  *      Purpose: Return the number of rows affected by the query (update,
357  *             or insert)
358  *
359  *************************************************************************/
360 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
361
362         SQLINTEGER count;
363         rlm_sql_iodbc_conn_t *conn = handle->conn;
364
365         SQLRowCount(conn->stmt_handle, &count);
366         return (int)count;
367 }
368
369 /* Exported to rlm_sql */
370 rlm_sql_module_t rlm_sql_iodbc = {
371         "rlm_sql_iodbc",
372         NULL,
373         sql_socket_init,
374         sql_query,
375         sql_select_query,
376         sql_store_result,
377         sql_num_fields,
378         sql_num_rows,
379         sql_fetch_row,
380         sql_free_result,
381         sql_error,
382         sql_finish_query,
383         sql_finish_select_query,
384         sql_affected_rows
385 };