update otp_hotp() to support 6,7,8,9 digit otp's
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_sybase / sql_sybase.c
1 /*
2  * sql_sybase.c Sybase (ctlibrary) routines for rlm_sql
3  *              Error handling stolen from Sybase example code "firstapp.c"
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *   GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program; if not, write to the Free Software
17  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  *
19  * Copyright 2000  The FreeRADIUS server project
20  * Copyright 2000  Mattias Sjostrom <mattias@nogui.se>
21  */
22
23 #include <freeradius-devel/autoconf.h>
24
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include        <freeradius-devel/radiusd.h>
31
32 #include <ctpublic.h>
33 #include "rlm_sql.h"
34
35
36 typedef struct rlm_sql_sybase_sock {
37         CS_CONTEXT      *context;
38         CS_CONNECTION   *connection;
39         CS_COMMAND      *command;
40         char            **results;
41         int             id;
42         int             in_use;
43         struct timeval  tv;
44 } rlm_sql_sybase_sock;
45
46
47 #define MAX_DATASTR_LEN 256
48
49 /************************************************************************
50 * Handler for server messages. Client-Library will call this
51 * routine when it receives a message from the server.
52 ************************************************************************/
53
54 static CS_RETCODE CS_PUBLIC
55 servermsg_callback(cp, chp, msgp)
56 CS_CONTEXT         *cp;
57 CS_CONNECTION      *chp;
58 CS_SERVERMSG       *msgp;
59 {
60
61         /*
62         ** Print the message info.
63         */
64         radlog(L_ERR,
65                 "Sybase Server message:\n");
66         radlog(L_ERR,
67                 "number(%ld) severity(%ld) state(%ld) line(%ld)\n",
68                 (long)msgp->msgnumber, (long)msgp->severity,
69                 (long)msgp->state, (long)msgp->line);
70
71         /*
72         ** Print the server and procedure names if supplied.
73         */
74         if (msgp->svrnlen > 0 && msgp->proclen > 0)
75                 radlog(L_ERR, "Server name: %s   Procedure name: %s", msgp->svrname, msgp->proc);
76
77         /*
78         ** Print the null terminated message.
79         */
80         radlog(L_ERR, "%s\n", msgp->text);
81
82         /*
83         ** Server message callbacks must return CS_SUCCEED.
84         */
85         return (CS_SUCCEED);
86 }
87
88 /************************************************************************
89 *  Client-Library error handler.
90 ************************************************************************/
91
92 static CS_RETCODE CS_PUBLIC
93 clientmsg_callback(context, conn, emsgp)
94 CS_CONTEXT         *context;
95 CS_CONNECTION      *conn;
96 CS_CLIENTMSG       *emsgp;
97 {
98
99         /*
100         ** Error number: Print the error's severity, number, origin, and
101         ** layer. These four numbers uniquely identify the error.
102         */
103         radlog(L_ERR,
104                 "Client Library error:\n");
105         radlog(L_ERR,
106                 "severity(%ld) number(%ld) origin(%ld) layer(%ld)\n",
107                 (long)CS_SEVERITY(emsgp->severity),
108                 (long)CS_NUMBER(emsgp->msgnumber),
109                 (long)CS_ORIGIN(emsgp->msgnumber),
110                 (long)CS_LAYER(emsgp->msgnumber));
111
112         /*
113         ** Error text: Print the error text.
114         */
115         radlog(L_ERR, "%s\n", emsgp->msgstring);
116
117         if (emsgp->osstringlen > 0)
118         {
119                 radlog(L_ERR,
120                         "Operating system error number(%ld):\n",
121                         (long)emsgp->osnumber);
122                 radlog(L_ERR, "%s\n", emsgp->osstring);
123         }
124
125         return (CS_SUCCEED);
126 }
127
128 /************************************************************************
129 *  CS-Library error handler. This function will be invoked
130 *  when CS-Library has detected an error.
131 ************************************************************************/
132
133 static CS_RETCODE CS_PUBLIC
134 csmsg_callback(context, emsgp)
135 CS_CONTEXT         *context;
136 CS_CLIENTMSG       *emsgp;
137 {
138
139         /*
140         ** Print the error number and message.
141         */
142         radlog(L_ERR,
143                 "CS-Library error:\n");
144         radlog(L_ERR,
145                 "\tseverity(%ld) layer(%ld) origin(%ld) number(%ld)",
146                 (long)CS_SEVERITY(emsgp->msgnumber),
147                 (long)CS_LAYER(emsgp->msgnumber),
148                 (long)CS_ORIGIN(emsgp->msgnumber),
149                 (long)CS_NUMBER(emsgp->msgnumber));
150
151         radlog(L_ERR, "%s\n", emsgp->msgstring);
152
153         /*
154         ** Print any operating system error information.
155         */
156         if (emsgp->osstringlen > 0)
157         {
158                 radlog(L_ERR, "Operating System Error: %s\n",
159                         emsgp->osstring);
160         }
161
162         return (CS_SUCCEED);
163 }
164
165 /*************************************************************************
166  *
167  *      Function: sql_init_socket
168  *
169  *      Purpose: Establish connection to the db
170  *
171  *************************************************************************/
172 static int sql_init_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
173
174         rlm_sql_sybase_sock *sybase_sock;
175
176
177         if (!sqlsocket->conn) {
178                 sqlsocket->conn = (rlm_sql_sybase_sock *)rad_malloc(sizeof(rlm_sql_sybase_sock));
179                 if (!sqlsocket->conn) {
180                         return -1;
181                 }
182         }
183         sybase_sock = sqlsocket->conn;
184         memset(sybase_sock, 0, sizeof(*sybase_sock));
185
186         sybase_sock->results=NULL;
187
188         /* Allocate a CS context structure. This should really only be done once, but because of
189            the connection pooling design of rlm_sql, we'll have to go with one context per connection */
190
191         if (cs_ctx_alloc(CS_VERSION_100, &sybase_sock->context) != CS_SUCCEED) {
192                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to allocate CS context structure (cs_ctx_alloc())");
193                 return -1;
194         }
195
196         /* Initialize ctlib */
197
198         if (ct_init(sybase_sock->context, CS_VERSION_100) != CS_SUCCEED) {
199                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to initialize Client-Library (ct_init())");
200                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
201                         cs_ctx_drop(sybase_sock->context);
202                 }
203                 return -1;
204         }
205
206         /* Install callback functions for error-handling */
207
208         if (cs_config(sybase_sock->context, CS_SET, CS_MESSAGE_CB, (CS_VOID *)csmsg_callback, CS_UNUSED, NULL) != CS_SUCCEED) {
209                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install CS Library error callback");
210                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
211                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
212                         cs_ctx_drop(sybase_sock->context);
213                 }
214                 return -1;
215         }
216
217         if (ct_callback(sybase_sock->context, NULL, CS_SET, CS_CLIENTMSG_CB, (CS_VOID *)clientmsg_callback) != CS_SUCCEED) {
218                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install client message callback");
219                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
220                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
221                         cs_ctx_drop(sybase_sock->context);
222                 }
223                 return -1;
224         }
225
226         if (ct_callback(sybase_sock->context, NULL, CS_SET, CS_SERVERMSG_CB, (CS_VOID *)servermsg_callback) != CS_SUCCEED) {
227                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to install client message callback");
228                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
229                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
230                         cs_ctx_drop(sybase_sock->context);
231                 }
232                 return -1;
233         }
234
235         /* Allocate a ctlib connection structure */
236
237         if (ct_con_alloc(sybase_sock->context, &sybase_sock->connection) != CS_SUCCEED) {
238                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to allocate connection structure (ct_con_alloc())");
239                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
240                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
241                         cs_ctx_drop(sybase_sock->context);
242                 }
243                 return -1;
244         }
245
246         /* Initialize inline error handling for the connection */
247
248 /*      if (ct_diag(sybase_sock->connection, CS_INIT, CS_UNUSED, CS_UNUSED, NULL) != CS_SUCCEED) {
249                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to initialize error handling (ct_diag())");
250                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
251                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
252                         cs_ctx_drop(sybase_sock->context);
253                 }
254                 return -1;
255         } */
256
257
258
259         /* Set User and Password properties for the connection */
260
261         if (ct_con_props(sybase_sock->connection, CS_SET, CS_USERNAME, config->sql_login,
262                                          strlen(config->sql_login), NULL) != CS_SUCCEED) {
263                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to set username for connection (ct_con_props())\n%s",
264                 sql_error(sqlsocket, config));
265                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
266                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
267                         cs_ctx_drop(sybase_sock->context);
268                 }
269                 return -1;
270         }
271
272         if (ct_con_props(sybase_sock->connection, CS_SET, CS_PASSWORD, config->sql_password,
273                                         strlen(config->sql_password), NULL) != CS_SUCCEED) {
274                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to set password for connection (ct_con_props())\n%s",
275                 sql_error(sqlsocket, config));
276                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
277                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
278                         cs_ctx_drop(sybase_sock->context);
279                 }
280                 return -1;
281         }
282
283         /* Establish the connection */
284
285         if (ct_connect(sybase_sock->connection, config->sql_server, strlen(config->sql_server)) != CS_SUCCEED) {
286                 radlog(L_ERR,"rlm_sql_sybase(sql_init_socket): Unable to establish connection to symbolic servername %s\n%s",
287                                 config->sql_server, sql_error(sqlsocket, config));
288                 if (sybase_sock->context != (CS_CONTEXT *)NULL) {
289                         ct_exit(sybase_sock->context, CS_FORCE_EXIT);
290                         cs_ctx_drop(sybase_sock->context);
291                 }
292                 return -1;
293         }
294         return 0;
295 }
296
297
298 /*************************************************************************
299  *
300  *      Function: sql_destroy_socket
301  *
302  *      Purpose: Free socket and private connection data
303  *
304  *************************************************************************/
305 static int sql_destroy_socket(SQLSOCK *sqlsocket, SQL_CONFIG *config)
306 {
307         free(sqlsocket->conn);
308         sqlsocket->conn = NULL;
309         return 0;
310 }
311
312 /*************************************************************************
313  *
314  *      Function: sql_query
315  *
316  *      Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
317  *               the database.
318  *
319  *************************************************************************/
320 static int sql_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
321
322         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
323
324         CS_RETCODE      ret, results_ret;
325         CS_INT          result_type;
326
327         if (config->sqltrace)
328                 DEBUG(querystr);
329          if (sybase_sock->connection == NULL) {
330                 radlog(L_ERR, "Socket not connected");
331                 return -1;
332         }
333
334         if (ct_cmd_alloc(sybase_sock->connection, &sybase_sock->command) != CS_SUCCEED) {
335                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to allocate command structure (ct_cmd_alloc())\n%s",
336                                 sql_error(sqlsocket, config));
337                 return -1;
338         }
339
340         if (ct_command(sybase_sock->command, CS_LANG_CMD, querystr, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
341                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to initiate command structure (ct_command())\n%s",
342                                 sql_error(sqlsocket, config));
343                 return -1;
344         }
345
346         if (ct_send(sybase_sock->command) != CS_SUCCEED) {
347                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Unable to send command (ct_send())\n%s",
348                                 sql_error(sqlsocket, config));
349                 return -1;
350         }
351
352         /*
353         ** We'll make three calls to ct_results, first to get a success indicator, secondly to get a done indicator, and
354         ** thirdly to get a "nothing left to handle" status.
355         */
356
357         /*
358         ** First call to ct_results,
359         ** we need returncode CS_SUCCEED
360         ** and result_type CS_CMD_SUCCEED.
361         */
362
363         if ((results_ret = ct_results(sybase_sock->command, &result_type)) == CS_SUCCEED) {
364                 if (result_type != CS_CMD_SUCCEED) {
365                         if  (result_type == CS_ROW_RESULT) {
366                                 radlog(L_ERR,"rlm_sql_sybase(sql_query): sql_query processed a query returning rows. Use sql_select_query instead!");
367                         }
368                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Result failure or unexpected result type from query\n%s",
369                                          sql_error(sqlsocket, config));
370                         return -1;
371                 }
372         }
373         else {
374                 switch ((int) results_ret)
375                 {
376
377                 case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */
378                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
379                                         , sql_error(sqlsocket, config));
380                         if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
381                                 radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
382                                 ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
383                                 sql_close(sqlsocket, config);
384                         }
385                         return -1;
386                         break;
387
388                 default:
389                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
390                                         sql_error(sqlsocket, config));
391                         return -1;
392                 }
393         }
394
395
396         /*
397         ** Second call to ct_results,
398         ** we need returncode CS_SUCCEED
399         ** and result_type CS_CMD_DONE.
400         */
401
402         if ((results_ret = ct_results(sybase_sock->command, &result_type)) == CS_SUCCEED) {
403                 if (result_type != CS_CMD_DONE) {
404                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Result failure or unexpected result type from query\n%s",
405                                          sql_error(sqlsocket, config));
406                         return -1;
407                 }
408         }
409         else {
410                 switch ((int) results_ret)
411                 {
412
413                 case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */
414                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
415                                         , sql_error(sqlsocket, config));
416                         if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
417                                 radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
418                                 ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
419                                 sql_close(sqlsocket, config);
420                         }
421                         return -1;
422                         break;
423
424                 default:
425                         radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
426                                         sql_error(sqlsocket, config));
427                         return -1;
428                 }
429         }
430
431
432         /*
433         ** Third call to ct_results,
434         ** we need returncode CS_END_RESULTS
435         ** result_type will be ignored.
436         */
437
438         results_ret = ct_results(sybase_sock->command, &result_type);
439
440         switch ((int) results_ret)
441         {
442
443         case CS_FAIL: /* Serious failure, sybase requires us to cancel and maybe even close connection */
444                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Failure retrieving query results\n%s"
445                                 , sql_error(sqlsocket, config));
446                 if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
447                         radlog(L_ERR,"rlm_sql_sybase(sql_query): cleaning up.");
448                         ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
449                         sql_close(sqlsocket, config);
450                 }
451                 return -1;
452                 break;
453
454         case CS_END_RESULTS:  /* This is where we want to end up */
455                 break;
456
457         default:
458                 radlog(L_ERR,"rlm_sql_sybase(sql_query): Unexpected return value from ct_results()\n%s",
459                                 sql_error(sqlsocket, config));
460                 return -1;
461                 break;
462         }
463         return 0;
464 }
465
466
467
468
469 /*************************************************************************
470  *
471  *      Function: sql_select_query
472  *
473  *      Purpose: Issue a select query to the database
474  *
475  *      Note: Only the first row from queries returning several rows
476  *            will be returned by this function, consequitive rows will
477  *            be discarded.
478  *
479  *************************************************************************/
480 static int sql_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config, char *querystr) {
481
482         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
483
484         CS_RETCODE      ret, results_ret;
485         CS_INT          result_type;
486         CS_DATAFMT      descriptor;
487
488         int             colcount,i;
489         char            **rowdata;
490
491         if (config->sqltrace)
492                 DEBUG(querystr);
493          if (sybase_sock->connection == NULL) {
494                 radlog(L_ERR, "Socket not connected");
495                 return -1;
496         }
497
498
499         if (ct_cmd_alloc(sybase_sock->connection, &sybase_sock->command) != CS_SUCCEED) {
500                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to allocate command structure (ct_cmd_alloc())\n%s",
501                                 sql_error(sqlsocket, config));
502                 return -1;
503         }
504
505         if (ct_command(sybase_sock->command, CS_LANG_CMD, querystr, CS_NULLTERM, CS_UNUSED) != CS_SUCCEED) {
506                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to initiate command structure (ct_command())\n%s",
507                                 sql_error(sqlsocket, config));
508                 return -1;
509         }
510
511         if (ct_send(sybase_sock->command) != CS_SUCCEED) {
512                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unable to send command (ct_send())\n%s",
513                                 sql_error(sqlsocket, config));
514                 return -1;
515         }
516
517         results_ret = ct_results(sybase_sock->command, &result_type);
518
519         switch (results_ret) {
520
521         case CS_SUCCEED:
522
523                 switch (result_type) {
524
525                 case CS_ROW_RESULT:
526
527                 /*
528                 ** Houston, we have a row.
529                 **
530                 ** We set up a target buffer for the results data, and
531                 ** associate the buffer with the results, but the actual
532                 ** fetching takes place in sql_fetch_row. The layer above
533                 ** MUST call sql_fetch_row and/or sql_finish_select_query
534                 ** or this socket will be unusable and may cause segfaults
535                 ** if reused later on.
536                 */
537
538                         /*
539                         ** Set up the DATAFMT structure that describes our target array
540                         ** and tells sybase what we want future ct_fetch calls to do.
541                         */
542                         descriptor.datatype = CS_CHAR_TYPE;     /* The target buffer is a string */
543                         descriptor.format = CS_FMT_NULLTERM;    /* Null termination please */
544                         descriptor.maxlength = MAX_DATASTR_LEN; /* The string arrays are this large */
545                         descriptor.count = 1;                   /* Fetch one row of data */
546                         descriptor.locale = NULL;               /* Don't do NLS stuff */
547
548
549                         colcount = sql_num_fields(sqlsocket, config); /* Get number of elements in row result */
550
551
552                         rowdata=(char **)rad_malloc(sizeof(char *) * (colcount+1));     /* Space for pointers */
553                         memset(rowdata, 0, (sizeof(char *) * colcount+1));  /* NULL-pad the pointers */
554
555                         for (i=0; i < colcount; i++) {
556
557                                 rowdata[i]=rad_malloc((MAX_DATASTR_LEN * sizeof(char))+1); /* Space to hold the result data */
558
559                                 /* Associate the target buffer with the data */
560                                 if (ct_bind(sybase_sock->command, i+1, &descriptor, rowdata[i], NULL, NULL) != CS_SUCCEED) {
561                                         radlog(L_ERR,"rlm_sql_sybase(sql_select_query): ct_bind() failed)\n%s",
562                                                         sql_error(sqlsocket, config));
563                                         return -1;
564                                 }
565
566                         }
567                         rowdata[i]=NULL; /* Terminate the array */
568                         sybase_sock->results=rowdata;
569                         break;
570
571                 case CS_CMD_SUCCEED:
572                 case CS_CMD_DONE:
573
574                         radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Query returned no data");
575                         break;
576
577                 default:
578
579                         radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unexpected result type from query\n%s",
580                                          sql_error(sqlsocket, config));
581                         sql_finish_select_query(sqlsocket, config);
582                         return -1;
583                         break;
584                 }
585                 break;
586
587         case CS_FAIL:
588
589                 /*
590                 ** Serious failure, sybase requires us to cancel
591                 ** the results and maybe even close the connection.
592                 */
593
594                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Failure retrieving query results\n%s"
595                                 , sql_error(sqlsocket, config));
596                 if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
597                         radlog(L_ERR,"rlm_sql_sybase(sql_select_query): cleaning up.");
598                         ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
599                         sql_close(sqlsocket, config);
600                 }
601                 return -1;
602                 break;
603
604         default:
605
606                 radlog(L_ERR,"rlm_sql_sybase(sql_select_query): Unexpected return value from ct_results()\n%s",
607                                 sql_error(sqlsocket, config));
608                 return -1;
609                 break;
610         }
611         return 0;
612 }
613
614
615 /*************************************************************************
616  *
617  *      Function: sql_store_result
618  *
619  *      Purpose: database specific store_result function. Returns a result
620  *               set for the query.
621  *
622  *************************************************************************/
623 static int sql_store_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
624         /*
625         ** Not needed for Sybase, code that may have gone here is
626         ** in sql_select_query and sql_fetch_row
627         */
628         return 0;
629 }
630
631
632 /*************************************************************************
633  *
634  *      Function: sql_num_fields
635  *
636  *      Purpose: database specific num_fields function. Returns number
637  *               of columns from query
638  *
639  *************************************************************************/
640 static int sql_num_fields(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
641
642         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
643         int     num;
644
645         if (ct_res_info(sybase_sock->command, CS_NUMDATA, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) {
646                 radlog(L_ERR,"rlm_sql_sybase(sql_num_fields): error retrieving column count: %s",
647                         sql_error(sqlsocket, config));
648                 return -1;
649         }
650         return num;
651 }
652
653 /*************************************************************************
654  *
655  *      Function: sql_num_rows
656  *
657  *      Purpose: database specific num_rows. Returns number of rows in
658  *               query
659  *
660  *************************************************************************/
661 static int sql_num_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
662
663         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
664         int     num;
665
666         if (ct_res_info(sybase_sock->command, CS_ROW_COUNT, (CS_INT *)&num, CS_UNUSED, NULL) != CS_SUCCEED) {
667                 radlog(L_ERR,"rlm_sql_sybase(sql_num_rows): error retrieving row count: %s",
668                         sql_error(sqlsocket, config));
669                 return -1;
670         }
671         return num;
672 }
673
674
675 /*************************************************************************
676  *
677  *      Function: sql_fetch_row
678  *
679  *      Purpose: database specific fetch_row. Returns a SQL_ROW struct
680  *               with all the data for the query in 'sqlsocket->row'. Returns
681  *               0 on success, -1 on failure, SQL_DOWN if 'database is down'.
682  *
683  *************************************************************************/
684 int sql_fetch_row(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
685
686         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
687         CS_INT          ret, count;
688
689         sqlsocket->row = NULL;
690
691
692         ret = ct_fetch(sybase_sock->command, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count);
693
694         switch (ret) {
695
696         case CS_FAIL:
697
698                 /*
699                 ** Serious failure, sybase requires us to cancel
700                 ** the results and maybe even close the connection.
701                 */
702
703                 radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Failure fething row data\n%s"
704                                 , sql_error(sqlsocket, config));
705                 if ((ret = ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL)) == CS_FAIL) {
706                         radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): cleaning up.");
707                         ct_close(sybase_sock->connection, CS_FORCE_CLOSE);
708                         sql_close(sqlsocket, config);
709                 }
710                 return SQL_DOWN;
711                 break;
712
713         case CS_END_DATA:
714
715                 return 0;
716                 break;
717
718         case CS_SUCCEED:
719
720                 sqlsocket->row = sybase_sock->results;
721                 return 0;
722                 break;
723
724         case CS_ROW_FAIL:
725
726                 radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Recoverable failure fething row data, try again perhaps?");
727                 return -1;
728
729         default:
730
731                 radlog(L_ERR,"rlm_sql_sybase(sql_fetch_row): Unexpected returncode from ct_fetch");
732                 return -1;
733                 break;
734         }
735
736 }
737
738
739
740 /*************************************************************************
741  *
742  *      Function: sql_free_result
743  *
744  *      Purpose: database specific free_result. Frees memory allocated
745  *               for a result set
746  *
747  *************************************************************************/
748 static int sql_free_result(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
749
750         /*
751         ** Not implemented, never called from rlm_sql anyway
752         ** result buffer is freed in the finish_query functions.
753         */
754
755         return 0;
756
757 }
758
759
760
761 /*************************************************************************
762  *
763  *      Function: sql_error
764  *
765  *      Purpose: database specific error. Returns error associated with
766  *               connection
767  *
768  *************************************************************************/
769 static char *sql_error(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
770         static char     msg='\0';
771 /*
772         static char     msgbuf[2048];
773
774         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
775         CS_INT          msgcount;
776         CS_CLIENTMSG    cmsg;
777         CS_SERVERMSG    smsg;
778
779         int             i;
780         char            ctempbuf[2][512];
781         char            stempbuf[2][512];
782
783         msgbuf[0]=(char)NULL;
784         ctempbuf[0][0]=(char)NULL;
785         ctempbuf[1][0]=(char)NULL;
786         stempbuf[0][0]=(char)NULL;
787         stempbuf[1][0]=(char)NULL;
788
789         if (ct_diag(sybase_sock->connection, CS_STATUS, CS_CLIENTMSG_TYPE, CS_UNUSED, &msgcount) != CS_SUCCEED) {
790                 radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to get number of pending Client messages");
791                 return msgbuf;
792         }
793         radlog(L_ERR,"rlm_sql_sybase(sql_error): Number of pending Client messages: %d", (int)msgcount);
794
795         for (i=1; i<=msgcount; i++) {
796                 if (ct_diag(sybase_sock->connection, CS_GET, CS_CLIENTMSG_TYPE, (CS_INT)i, &cmsg) != CS_SUCCEED) {
797                         radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to retrieve pending Client message");
798                         return msgbuf;
799                 }
800                 sprintf(ctempbuf[i-1],"rlm_sql_sybase: Client Library Error: severity(%ld) number(%ld) origin(%ld) layer(%ld):\n%s",
801                                 (long)CS_SEVERITY(cmsg.severity),
802                                 (long)CS_NUMBER(cmsg.msgnumber),
803                                 (long)CS_ORIGIN(cmsg.msgnumber),
804                                 (long)CS_LAYER(cmsg.msgnumber),
805                                 cmsg.msgstring);
806         }
807
808
809         if (ct_diag(sybase_sock->connection, CS_STATUS, CS_SERVERMSG_TYPE, CS_UNUSED, &msgcount) != CS_SUCCEED) {
810                 radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to get number of pending Server messages");
811                 return msgbuf;
812         }
813         radlog(L_ERR,"rlm_sql_sybase(sql_error): Number of pending Server messages: %d", (int)msgcount);
814
815         for (i=1; i<=msgcount; i++) {
816                 if (ct_diag(sybase_sock->connection, CS_GET, CS_SERVERMSG_TYPE, (CS_INT)i, &smsg) != CS_SUCCEED) {
817                         radlog(L_ERR,"rlm_sql_sybase(sql_error): Failed to retrieve pending Server message");
818                         return msgbuf;
819                 }
820                 sprintf(stempbuf[i-1],"rlm_sql_sybase: Server message: severity(%ld) number(%ld) origin(%ld) layer(%ld):\n%s",
821                                 (long)CS_SEVERITY(cmsg.severity),
822                                 (long)CS_NUMBER(cmsg.msgnumber),
823                                 (long)CS_ORIGIN(cmsg.msgnumber),
824                                 (long)CS_LAYER(cmsg.msgnumber),
825                                 cmsg.msgstring);
826         }
827         sprintf(msgbuf,"%s || %s || %s || %s", ctempbuf[1], ctempbuf[2], stempbuf[1], stempbuf[2]);
828
829         return msgbuf;
830 */
831         return &msg;
832 }
833
834
835 /*************************************************************************
836  *
837  *      Function: sql_close
838  *
839  *      Purpose: database specific close. Closes an open database
840  *               connection and cleans up any open handles.
841  *
842  *************************************************************************/
843 static int sql_close(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
844 /*
845         rlm_sql_oracle_sock *oracle_sock = sqlsocket->conn;
846
847         if (oracle_sock->conn) {
848                 OCILogoff (oracle_sock->conn, oracle_sock->errHandle);
849         }
850
851         if (oracle_sock->queryHandle) {
852                 OCIHandleFree((dvoid *)oracle_sock->queryHandle, (ub4) OCI_HTYPE_STMT);
853         }
854         if (oracle_sock->errHandle) {
855                 OCIHandleFree((dvoid *)oracle_sock->errHandle, (ub4) OCI_HTYPE_ERROR);
856         }
857         if (oracle_sock->env) {
858                 OCIHandleFree((dvoid *)oracle_sock->env, (ub4) OCI_HTYPE_ENV);
859         }
860
861         oracle_sock->conn = NULL;
862 */
863         return 0;
864 }
865
866
867 /*************************************************************************
868  *
869  *      Function: sql_finish_query
870  *
871  *      Purpose: End the query, such as freeing memory
872  *
873  *************************************************************************/
874 static int sql_finish_query(SQLSOCK *sqlsocket, SQL_CONFIG *config)
875 {
876         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
877
878         ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL);
879
880         if (ct_cmd_drop(sybase_sock->command) != CS_SUCCEED) {
881                 radlog(L_ERR,"rlm_sql_sybase(sql_finish_query): Freeing command structure failed.");
882                 return -1;
883         }
884
885         return 0;
886 }
887
888
889
890 /*************************************************************************
891  *
892  *      Function: sql_finish_select_query
893  *
894  *      Purpose: End the select query, such as freeing memory or result
895  *
896  *************************************************************************/
897 static int sql_finish_select_query(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
898
899         rlm_sql_sybase_sock *sybase_sock = sqlsocket->conn;
900         int     i=0;
901
902         ct_cancel(NULL, sybase_sock->command, CS_CANCEL_ALL);
903
904         if (ct_cmd_drop(sybase_sock->command) != CS_SUCCEED) {
905                 radlog(L_ERR,"rlm_sql_sybase(sql_finish_select_query): Freeing command structure failed.");
906                 return -1;
907         }
908
909         if (sybase_sock->results) {
910                 while(sybase_sock->results[i]) free(sybase_sock->results[i++]);
911                 free(sybase_sock->results);
912                 sybase_sock->results=NULL;
913         }
914
915         return 0;
916
917 }
918
919
920 /*************************************************************************
921  *
922  *      Function: sql_affected_rows
923  *
924  *      Purpose: Return the number of rows affected by the query (update,
925  *               or insert)
926  *
927  *************************************************************************/
928 static int sql_affected_rows(SQLSOCK *sqlsocket, SQL_CONFIG *config) {
929
930         return sql_num_rows(sqlsocket, config);
931
932 }
933
934
935
936
937 /* Exported to rlm_sql */
938 rlm_sql_module_t rlm_sql_sybase = {
939         "rlm_sql_sybase",
940         sql_init_socket,
941         sql_destroy_socket,
942         sql_query,
943         sql_select_query,
944         sql_store_result,
945         sql_num_fields,
946         sql_num_rows,
947         sql_fetch_row,
948         sql_free_result,
949         sql_error,
950         sql_close,
951         sql_finish_query,
952         sql_finish_select_query,
953         sql_affected_rows
954 };