Remove junk boilerplate text and whitespace from sql drivers to make future diffs...
[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 RCSID("$Id$")
25 USES_APPLE_DEPRECATED_API
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 #define IODBC_MAX_ERROR_LEN 256
38
39 typedef struct rlm_sql_iodbc_conn {
40         HENV    env_handle;
41         HDBC    dbc_handle;
42         HSTMT   stmt_handle;
43         int     id;
44
45         rlm_sql_row_t row;
46
47         struct sql_socket *next;
48
49         SQLCHAR error[IODBC_MAX_ERROR_LEN];
50         void    *conn;
51 } rlm_sql_iodbc_conn_t;
52
53 static char const *sql_error(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
54 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
55
56 static int _sql_socket_destructor(rlm_sql_iodbc_conn_t *conn)
57 {
58         DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
59
60         if (conn->stmt_handle) {
61                 SQLFreeStmt(conn->stmt_handle, SQL_DROP);
62         }
63
64         if (conn->dbc_handle) {
65                 SQLDisconnect(conn->dbc_handle);
66                 SQLFreeConnect(conn->dbc_handle);
67         }
68
69         if (conn->env_handle) {
70                 SQLFreeEnv(conn->env_handle);
71         }
72
73         return 0;
74 }
75
76 static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
77 {
78
79         rlm_sql_iodbc_conn_t *conn;
80         SQLRETURN rcode;
81
82         MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
83         talloc_set_destructor(conn, _sql_socket_destructor);
84
85         rcode = SQLAllocEnv(&conn->env_handle);
86         if (!SQL_SUCCEEDED(rcode)) {
87                 ERROR("rlm_sql_iodbc: SQLAllocEnv failed: %s", sql_error(handle, config));
88                 return -1;
89         }
90
91         rcode = SQLAllocConnect(conn->env_handle,
92                                 &conn->dbc_handle);
93         if (!SQL_SUCCEEDED(rcode)) {
94                 ERROR("rlm_sql_iodbc: SQLAllocConnect failed: %s", sql_error(handle, config));
95                 return -1;
96         }
97
98         /*
99          *      The iodbc API doesn't qualify arguments as const even when they should be.
100          */
101         {
102                 SQLCHAR *server, *login, *password;
103
104                 memcpy(&server, &config->sql_server, sizeof(server));
105                 memcpy(&login, &config->sql_login, sizeof(login));
106                 memcpy(&password, &config->sql_password, sizeof(password));
107
108                 rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS);
109         }
110         if (!SQL_SUCCEEDED(rcode)) {
111                 ERROR("rlm_sql_iodbc: SQLConnectfailed: %s", sql_error(handle, config));
112                 return -1;
113         }
114
115         return 0;
116 }
117
118 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
119 {
120         rlm_sql_iodbc_conn_t *conn = handle->conn;
121         SQLRETURN rcode;
122
123         rcode = SQLAllocStmt(conn->dbc_handle, &conn->stmt_handle);
124         if (!SQL_SUCCEEDED(rcode)) {
125                 ERROR("rlm_sql_iodbc: SQLAllocStmt failed: %s", sql_error(handle, config));
126                 return -1;
127         }
128
129         if (!conn->dbc_handle) {
130                 ERROR("rlm_sql_iodbc: Socket not connected");
131                 return -1;
132         }
133
134         {
135                 SQLCHAR *statement;
136
137                 memcpy(&statement, &query, sizeof(statement));
138                 rcode = SQLExecDirect(conn->stmt_handle, statement, SQL_NTS);
139         }
140
141         if (!SQL_SUCCEEDED(rcode)) {
142                 ERROR("rlm_sql_iodbc: Query failed %s", sql_error(handle, config));
143                 return -1;
144         }
145
146         return 0;
147 }
148
149 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
150 {
151         int numfields = 0;
152         int i = 0;
153         char **row = NULL;
154         long len = 0;
155         rlm_sql_iodbc_conn_t *conn = handle->conn;
156
157         if(sql_query(handle, config, query) < 0) {
158                 return -1;
159         }
160
161         numfields = sql_num_fields(handle, config);
162
163         row = (char **) rad_malloc(sizeof(char *) * (numfields+1));
164         memset(row, 0, (sizeof(char *) * (numfields)));
165         row[numfields] = NULL;
166
167         for(i=1; i<=numfields; i++) {
168                 SQLColAttributes(conn->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
169                 len++;
170
171                 /*
172                  * Allocate space for each column
173                  */
174                 row[i - 1] = rad_malloc((size_t) len);
175
176                 /*
177                  * This makes me feel dirty, but, according to Microsoft, it works.
178                  * Any ODBC datatype can be converted to a 'char *' according to
179                  * the following:
180                  *
181                  * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
182                  */
183                 SQLBindCol(conn->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
184         }
185
186         conn->row = row;
187
188         return 0;
189 }
190
191 static sql_rcode_t sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
192 {
193         return 0;
194 }
195
196 static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
197 {
198
199         SQLSMALLINT count=0;
200         rlm_sql_iodbc_conn_t *conn = handle->conn;
201
202         SQLNumResultCols(conn->stmt_handle, &count);
203
204         return (int)count;
205 }
206
207 static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
208 {
209         /*
210          * I presume this function is used to determine the number of
211          * rows in a result set *before* fetching them.  I don't think
212          * this is possible in ODBC 2.x, but I'd be happy to be proven
213          * wrong.  If you know how to do this, email me at jeff@apex.net
214          */
215         return 0;
216 }
217
218 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
219 {
220         SQLRETURN rc;
221         rlm_sql_iodbc_conn_t *conn = handle->conn;
222
223         handle->row = NULL;
224
225         if((rc = SQLFetch(conn->stmt_handle)) == SQL_NO_DATA_FOUND) {
226                 return 0;
227         }
228         /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
229
230         handle->row = conn->row;
231         return 0;
232 }
233
234 static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
235 {
236         int i = 0;
237         rlm_sql_iodbc_conn_t *conn = handle->conn;
238
239         for (i = 0; i < sql_num_fields(handle, config); i++) free(conn->row[i]);
240         free(conn->row);
241         conn->row = NULL;
242
243         SQLFreeStmt(conn->stmt_handle, SQL_DROP);
244
245         return 0;
246 }
247
248 static char const *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
249 {
250         SQLINTEGER errornum = 0;
251         SQLSMALLINT length = 0;
252         SQLCHAR state[256] = "";
253         rlm_sql_iodbc_conn_t *conn = handle->conn;
254
255         conn->error[0] = '\0';
256
257         SQLError(conn->env_handle, conn->dbc_handle, conn->stmt_handle,
258                 state, &errornum, conn->error, IODBC_MAX_ERROR_LEN, &length);
259         return (char const *) &conn->error;
260 }
261
262 static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
263 {
264         return sql_free_result(handle, config);
265 }
266
267 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
268 {
269         return sql_free_result(handle, config);
270 }
271
272 static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
273 {
274         long count;
275         rlm_sql_iodbc_conn_t *conn = handle->conn;
276
277         SQLRowCount(conn->stmt_handle, &count);
278         return (int)count;
279 }
280
281 /* Exported to rlm_sql */
282 extern rlm_sql_module_t rlm_sql_iodbc;
283 rlm_sql_module_t rlm_sql_iodbc = {
284         .name                           = "rlm_sql_iodbc",
285         .sql_socket_init                = sql_socket_init,
286         .sql_query                      = sql_query,
287         .sql_select_query               = sql_select_query,
288         .sql_store_result               = sql_store_result,
289         .sql_num_fields                 = sql_num_fields,
290         .sql_num_rows                   = sql_num_rows,
291         .sql_affected_rows              = sql_affected_rows,
292         .sql_fetch_row                  = sql_fetch_row,
293         .sql_free_result                = sql_free_result,
294         .sql_error                      = sql_error,
295         .sql_finish_query               = sql_finish_query,
296         .sql_finish_select_query        = sql_finish_select_query
297 };