Typo
[freeradius.git] / src / modules / rlm_sql / drivers / rlm_sql_iodbc / rlm_sql_iodbc.c
index 389ed70..4a4ae5c 100644 (file)
  * Copyright 2000  Jeff Carneal <jeff@apex.net>
  */
 
-#include <freeradius-devel/ident.h>
 RCSID("$Id$")
+USES_APPLE_DEPRECATED_API
 
 #include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
 
 #include <sys/stat.h>
 
@@ -34,12 +35,14 @@ RCSID("$Id$")
 
 #include "rlm_sql.h"
 
+#define IODBC_MAX_ERROR_LEN 256
+
 typedef struct rlm_sql_iodbc_conn {
        HENV    env_handle;
        HDBC    dbc_handle;
-       HSTMT   stmt_handle;
+       HSTMT   stmt;
        int     id;
-       
+
        rlm_sql_row_t row;
 
        struct sql_socket *next;
@@ -47,128 +50,109 @@ typedef struct rlm_sql_iodbc_conn {
        void    *conn;
 } rlm_sql_iodbc_conn_t;
 
-static const char *sql_error(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
+static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
+                       rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config);
 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t *config);
 
-static int sql_socket_destructor(void *c)
+static int _sql_socket_destructor(rlm_sql_iodbc_conn_t *conn)
 {
-       rlm_sql_iodbc_conn_t *conn = c;
-       
        DEBUG2("rlm_sql_iodbc: Socket destructor called, closing socket");
-       
-       if (conn->stmt_handle) {
-               SQLFreeStmt(conn->stmt_handle, SQL_DROP);
-       }
-       
+
+       if (conn->stmt) SQLFreeStmt(conn->stmt, SQL_DROP);
+
        if (conn->dbc_handle) {
                SQLDisconnect(conn->dbc_handle);
                SQLFreeConnect(conn->dbc_handle);
        }
-       
-       if (conn->env_handle) {
-               SQLFreeEnv(conn->env_handle);
-       }
-       
+
+       if (conn->env_handle) SQLFreeEnv(conn->env_handle);
+
        return 0;
 }
 
-/*************************************************************************
- *
- *     Function: sql_socket_init
- *
- *     Purpose: Establish connection to the db
- *
- *************************************************************************/
-static int sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
+static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
+{
 
        rlm_sql_iodbc_conn_t *conn;
        SQLRETURN rcode;
+       sql_log_entry_t entry;
 
        MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_iodbc_conn_t));
-       talloc_set_destructor((void *) conn, sql_socket_destructor);
+       talloc_set_destructor(conn, _sql_socket_destructor);
 
        rcode = SQLAllocEnv(&conn->env_handle);
        if (!SQL_SUCCEEDED(rcode)) {
-               radlog(L_ERR, "sql_create_socket: SQLAllocEnv failed:  %s",
-                               sql_error(handle, config));
-               return -1;
+               ERROR("rlm_sql_iodbc: SQLAllocEnv failed");
+               if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
+
+               return RLM_SQL_ERROR;
        }
 
        rcode = SQLAllocConnect(conn->env_handle,
                                &conn->dbc_handle);
        if (!SQL_SUCCEEDED(rcode)) {
-               radlog(L_ERR, "sql_create_socket: SQLAllocConnect failed:  %s",
-                               sql_error(handle, config));
-               return -1;
+               ERROR("rlm_sql_iodbc: SQLAllocConnect failed");
+               if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
+
+               return RLM_SQL_ERROR;
        }
 
-       rcode = SQLConnect(conn->dbc_handle, config->sql_server,
-                          SQL_NTS, config->sql_login, SQL_NTS,
-                          config->sql_password, SQL_NTS);
+       /*
+        *      The iodbc API doesn't qualify arguments as const even when they should be.
+        */
+       {
+               SQLCHAR *server, *login, *password;
+
+               memcpy(&server, &config->sql_server, sizeof(server));
+               memcpy(&login, &config->sql_login, sizeof(login));
+               memcpy(&password, &config->sql_password, sizeof(password));
+
+               rcode = SQLConnect(conn->dbc_handle, server, SQL_NTS, login, SQL_NTS, password, SQL_NTS);
+       }
        if (!SQL_SUCCEEDED(rcode)) {
-               radlog(L_ERR, "sql_create_socket: SQLConnectfailed:  %s",
-                               sql_error(handle, config));
-               return -1;
+               ERROR("rlm_sql_iodbc: SQLConnectfailed");
+               if (sql_error(NULL, &entry, 1, handle, config) > 0) ERROR("rlm_sql_iodbc: %s", entry.msg);
+
+               return RLM_SQL_ERROR;
        }
 
        return 0;
 }
 
-/*************************************************************************
- *
- *     Function: sql_query
- *
- *     Purpose: Issue a non-SELECT query (ie: update/delete/insert) to
- *            the database.
- *
- *************************************************************************/
-static int sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char *querystr) {
-
+static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, char const *query)
+{
        rlm_sql_iodbc_conn_t *conn = handle->conn;
        SQLRETURN rcode;
 
-       rcode = SQLAllocStmt(conn->dbc_handle,
-                            &conn->stmt_handle);
-       if (!SQL_SUCCEEDED(rcode)) {
-               radlog(L_ERR, "sql_create_socket: SQLAllocStmt failed:  %s",
-                               sql_error(handle, config));
-               return -1;
-       }
+       rcode = SQLAllocStmt(conn->dbc_handle, &conn->stmt);
+       if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR;
 
-       if (conn->dbc_handle == NULL) {
-               radlog(L_ERR, "sql_query:  Socket not connected");
-               return -1;
+       if (!conn->dbc_handle) {
+               ERROR("rlm_sql_iodbc: Socket not connected");
+               return RLM_SQL_ERROR;
        }
 
-       rcode = SQLExecDirect(conn->stmt_handle, querystr, SQL_NTS);
-       if (!SQL_SUCCEEDED(rcode)) {
-               radlog(L_ERR, "sql_query: failed:  %s",
-                               sql_error(handle, config));
-               return -1;
+       {
+               SQLCHAR *statement;
+
+               memcpy(&statement, &query, sizeof(statement));
+               rcode = SQLExecDirect(conn->stmt, statement, SQL_NTS);
        }
 
+       if (!SQL_SUCCEEDED(rcode)) return RLM_SQL_ERROR;
+
        return 0;
 }
 
-
-/*************************************************************************
- *
- *     Function: sql_select_query
- *
- *     Purpose: Issue a select query to the database
- *
- *************************************************************************/
-static int sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char *querystr) {
-
+static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
+{
        int numfields = 0;
-       int i=0;
-       char **row=NULL;
-       SQLINTEGER len=0;
+       int i = 0;
+       char **row = NULL;
+       long len = 0;
        rlm_sql_iodbc_conn_t *conn = handle->conn;
 
-       if(sql_query(handle, config, querystr) < 0) {
-               return -1;
-       }
+       if (sql_query(handle, config, query) < 0) return RLM_SQL_ERROR;
 
        numfields = sql_num_fields(handle, config);
 
@@ -177,14 +161,13 @@ static int sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config,
        row[numfields] = NULL;
 
        for(i=1; i<=numfields; i++) {
-               SQLColAttributes(conn->stmt_handle, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH,
-                                                                               NULL, 0, NULL, &len);
+               SQLColAttributes(conn->stmt, ((SQLUSMALLINT) i), SQL_COLUMN_LENGTH, NULL, 0, NULL, &len);
                len++;
 
                /*
                 * Allocate space for each column
                 */
-               row[i-1] = (SQLCHAR*)rad_malloc((int)len);
+               row[i - 1] = rad_malloc((size_t) len);
 
                /*
                 * This makes me feel dirty, but, according to Microsoft, it works.
@@ -193,7 +176,7 @@ static int sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config,
                 *
                 * http://msdn.microsoft.com/library/psdk/dasdk/odap4o4z.htm
                 */
-               SQLBindCol(conn->stmt_handle, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
+               SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)row[i-1], len, 0);
        }
 
        conn->row = row;
@@ -201,185 +184,150 @@ static int sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config,
        return 0;
 }
 
+static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
 
-/*************************************************************************
- *
- *     Function: sql_store_result
- *
- *     Purpose: database specific store_result function. Returns a result
- *            set for the query.
- *
- *************************************************************************/
-static int sql_store_result(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
+       SQLSMALLINT count=0;
+       rlm_sql_iodbc_conn_t *conn = handle->conn;
 
-       return 0;
+       SQLNumResultCols(conn->stmt, &count);
+
+       return (int)count;
 }
 
+static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       rlm_sql_iodbc_conn_t *conn = handle->conn;
 
-/*************************************************************************
- *
- *     Function: sql_num_fields
- *
- *     Purpose: database specific num_fields function. Returns number
- *            of columns from query
- *
- *************************************************************************/
-static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
+       SQLSMALLINT     fields, len, i;
 
-       SQLSMALLINT count=0;
-       rlm_sql_iodbc_conn_t *conn = handle->conn;
+       char const      **names;
+       char            field[128];
 
-       SQLNumResultCols(conn->stmt_handle, &count);
+       SQLNumResultCols(conn->stmt, &fields);
+       if (fields == 0) return RLM_SQL_ERROR;
 
-       return (int)count;
-}
+       MEM(names = talloc_array(handle, char const *, fields));
 
-/*************************************************************************
- *
- *     Function: sql_num_rows
- *
- *     Purpose: database specific num_rows. Returns number of rows in
- *            query
- *
- *************************************************************************/
-static int sql_num_rows(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
-       /*
-        * I presume this function is used to determine the number of
-        * rows in a result set *before* fetching them.  I don't think
-        * this is possible in ODBC 2.x, but I'd be happy to be proven
-        * wrong.  If you know how to do this, email me at jeff@apex.net
-        */
-       return 0;
-}
+       for (i = 0; i < fields; i++) {
+               char *p;
 
+               switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
+                                       field, sizeof(field), &len, NULL)) {
+               case SQL_INVALID_HANDLE:
+               case SQL_ERROR:
+                       ERROR("Failed retrieving field name at index %i", i);
+                       talloc_free(names);
+                       return RLM_SQL_ERROR;
 
-/*************************************************************************
- *
- *     Function: sql_fetch_row
- *
- *     Purpose: database specific fetch_row. Returns a rlm_sql_row_t struct
- *            with all the data for the query in 'handle->row'. Returns
- *              0 on success, -1 on failure, SQL_DOWN if 'database is down'
- *
- *************************************************************************/
-static int sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
+               default:
+                       break;
+               }
+
+               MEM(p = talloc_array(names, char, (size_t)len + 1));
+               strlcpy(p, field, (size_t)len + 1);
+               names[i] = p;
+       }
+       *out = names;
 
+       return RLM_SQL_OK;
+}
+
+static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
        SQLRETURN rc;
        rlm_sql_iodbc_conn_t *conn = handle->conn;
 
        handle->row = NULL;
 
-       if((rc = SQLFetch(conn->stmt_handle)) == SQL_NO_DATA_FOUND) {
-               return 0;
-       }
-       /* XXX Check rc for database down, if so, return SQL_DOWN */
+       rc = SQLFetch(conn->stmt);
+       if (rc == SQL_NO_DATA_FOUND) return RLM_SQL_NO_MORE_ROWS;
+
+       /* XXX Check rc for database down, if so, return RLM_SQL_RECONNECT */
 
        handle->row = conn->row;
        return 0;
 }
 
-
-
-/*************************************************************************
- *
- *     Function: sql_free_result
- *
- *     Purpose: database specific free_result. Frees memory allocated
- *            for a result set
- *
- *************************************************************************/
-static int sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
-
-       int i=0;
+static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
+{
+       int i = 0;
        rlm_sql_iodbc_conn_t *conn = handle->conn;
 
-       for(i=0; i<sql_num_fields(handle, config); i++) {
-               free(conn->row[i]);
-       }
+       for (i = 0; i < sql_num_fields(handle, config); i++) free(conn->row[i]);
        free(conn->row);
-       conn->row=NULL;
+       conn->row = NULL;
 
-       SQLFreeStmt( conn->stmt_handle, SQL_DROP );
+       SQLFreeStmt(conn->stmt, SQL_DROP);
 
        return 0;
 }
 
-
-/*************************************************************************
- *
- *     Function: sql_error
+/** Retrieves any errors associated with the connection handle
  *
- *     Purpose: database specific error. Returns error associated with
- *            connection
+ * @note Caller will free any memory allocated in ctx.
  *
- *************************************************************************/
-static const char *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
+ * @param ctx to allocate temporary error buffers in.
+ * @param out Array of sql_log_entrys to fill.
+ * @param outlen Length of out array.
+ * @param handle rlm_sql connection handle.
+ * @param config rlm_sql config.
+ * @return number of errors written to the sql_log_entry array.
+ */
+static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen,
+                       rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       rlm_sql_iodbc_conn_t    *conn = handle->conn;
+       SQLINTEGER              errornum = 0;
+       SQLSMALLINT             length = 0;
+       SQLCHAR                 state[256] = "";
+       SQLCHAR                 errbuff[IODBC_MAX_ERROR_LEN];
 
-       SQLINTEGER errornum = 0;
-       SQLSMALLINT length = 0;
-       SQLCHAR state[256] = "";
-       static SQLCHAR error[256] = "";
-       rlm_sql_iodbc_conn_t *conn = handle->conn;
+       rad_assert(outlen > 0);
 
-       SQLError(conn->env_handle, conn->dbc_handle, conn->stmt_handle,
-               state, &errornum, error, 256, &length);
-       return error;
-}
+       errbuff[0] = '\0';
+       SQLError(conn->env_handle, conn->dbc_handle, conn->stmt,
+                state, &errornum, errbuff, IODBC_MAX_ERROR_LEN, &length);
+       if (errbuff[0] == '\0') return 0;
 
-/*************************************************************************
- *
- *     Function: sql_finish_query
- *
- *     Purpose: End the query, such as freeing memory
- *
- *************************************************************************/
-static int sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
+       out[0].type = L_ERR;
+       out[0].msg = talloc_asprintf(ctx, "%s: %s", state, errbuff);
 
-       return sql_free_result(handle, config);
+       return 1;
 }
 
-/*************************************************************************
- *
- *     Function: sql_finish_select_query
- *
- *     Purpose: End the select query, such as freeing memory or result
- *
- *************************************************************************/
-static int sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
+static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
+{
        return sql_free_result(handle, config);
 }
 
-/*************************************************************************
- *
- *     Function: sql_affected_rows
- *
- *     Purpose: Return the number of rows affected by the query (update,
- *            or insert)
- *
- *************************************************************************/
-static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config) {
+static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
+{
+       return sql_free_result(handle, config);
+}
 
-       SQLINTEGER count;
+static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
+{
+       long count;
        rlm_sql_iodbc_conn_t *conn = handle->conn;
 
-       SQLRowCount(conn->stmt_handle, &count);
+       SQLRowCount(conn->stmt, &count);
        return (int)count;
 }
 
 /* Exported to rlm_sql */
+extern rlm_sql_module_t rlm_sql_iodbc;
 rlm_sql_module_t rlm_sql_iodbc = {
-       "rlm_sql_iodbc",
-       NULL,
-       sql_socket_init,
-       sql_query,
-       sql_select_query,
-       sql_store_result,
-       sql_num_fields,
-       sql_num_rows,
-       sql_fetch_row,
-       sql_free_result,
-       sql_error,
-       sql_finish_query,
-       sql_finish_select_query,
-       sql_affected_rows
+       .name                           = "rlm_sql_iodbc",
+       .sql_socket_init                = sql_socket_init,
+       .sql_query                      = sql_query,
+       .sql_select_query               = sql_select_query,
+       .sql_num_fields                 = sql_num_fields,
+       .sql_affected_rows              = sql_affected_rows,
+       .sql_fields                     = sql_fields,
+       .sql_fetch_row                  = sql_fetch_row,
+       .sql_free_result                = sql_free_result,
+       .sql_error                      = sql_error,
+       .sql_finish_query               = sql_finish_query,
+       .sql_finish_select_query        = sql_finish_select_query
 };