Thu Apr 28 2011 17:13:55

Asterisk developer's documentation


func_odbc.c File Reference

ODBC lookups. More...

#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/res_odbc.h"
#include "asterisk/app.h"
#include "asterisk/cli.h"
#include "asterisk/strings.h"
Include dependency graph for func_odbc.c:

Go to the source code of this file.

Data Structures

struct  acf_odbc_query
struct  odbc_datastore
struct  odbc_datastore_row
struct  queries

Enumerations

enum  { OPT_ESCAPECOMMAS = (1 << 0), OPT_MULTIROW = (1 << 1) }

Functions

static void __fini_queries (void)
static void __init_queries (void)
static void __reg_module (void)
static void __unreg_module (void)
static int acf_escape (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int acf_fetch (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int acf_odbc_read (struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
static int acf_odbc_write (struct ast_channel *chan, const char *cmd, char *s, const char *value)
 AST_THREADSTORAGE_CUSTOM_SCOPE (sql_buf, NULL, ast_free_ptr, static)
 AST_THREADSTORAGE_CUSTOM_SCOPE (sql2_buf, NULL, ast_free_ptr, static)
 AST_THREADSTORAGE_CUSTOM_SCOPE (coldata_buf, NULL, ast_free_ptr, static)
 AST_THREADSTORAGE_CUSTOM_SCOPE (colnames_buf, NULL, ast_free_ptr, static)
static char * cli_odbc_read (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * cli_odbc_write (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int exec_odbcfinish (struct ast_channel *chan, void *data)
static int free_acf_query (struct acf_odbc_query *query)
static SQLHSTMT generic_execute (struct odbc_obj *obj, void *data)
static int init_acf_query (struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
static int load_module (void)
static void odbc_datastore_free (void *data)
static int reload (void)
static int unload_module (void)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "ODBC lookups" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, }
static char * app_odbcfinish = "ODBCFinish"
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_func_odbc []
static char * config = "func_odbc.conf"
static struct ast_custom_function escape_function
static struct ast_custom_function fetch_function
struct ast_datastore_info odbc_info
enum { ... }  odbc_option_flags
struct queries queries
static int resultcount = 0

Detailed Description

ODBC lookups.

Author:
Tilghman Lesher <func_odbc__200508@the-tilghman.com>

Definition in file func_odbc.c.


Enumeration Type Documentation

anonymous enum
Enumerator:
OPT_ESCAPECOMMAS 
OPT_MULTIROW 

Definition at line 102 of file func_odbc.c.

     {
   OPT_ESCAPECOMMAS =   (1 << 0),
   OPT_MULTIROW     =   (1 << 1),
} odbc_option_flags;

Function Documentation

static void __fini_queries ( void  ) [static]

Definition at line 138 of file func_odbc.c.

{
static void __init_queries ( void  ) [static]

Definition at line 138 of file func_odbc.c.

{
static void __reg_module ( void  ) [static]

Definition at line 1476 of file func_odbc.c.

static void __unreg_module ( void  ) [static]

Definition at line 1476 of file func_odbc.c.

static int acf_escape ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 704 of file func_odbc.c.

References buf, odbc_datastore_row::data, and len().

{
   char *out = buf;

   for (; *data && out - buf < len; data++) {
      if (*data == '\'') {
         *out = '\'';
         out++;
      }
      *out++ = *data;
   }
   *out = '\0';

   return 0;
}
static int acf_fetch ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 726 of file func_odbc.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_copy_string(), ast_datastore_free(), ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, odbc_datastore_row::data, ast_datastore::data, odbc_datastore_row::list, odbc_datastore::names, and pbx_builtin_setvar_helper().

{
   struct ast_datastore *store;
   struct odbc_datastore *resultset;
   struct odbc_datastore_row *row;
   store = ast_channel_datastore_find(chan, &odbc_info, data);
   if (!store) {
      pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
      return -1;
   }
   resultset = store->data;
   AST_LIST_LOCK(resultset);
   row = AST_LIST_REMOVE_HEAD(resultset, list);
   AST_LIST_UNLOCK(resultset);
   if (!row) {
      /* Cleanup datastore */
      ast_channel_datastore_remove(chan, store);
      ast_datastore_free(store);
      pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
      return -1;
   }
   pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
   ast_copy_string(buf, row->data, len);
   ast_free(row);
   pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
   return 0;
}
static int acf_odbc_read ( struct ast_channel chan,
const char *  cmd,
char *  s,
char *  buf,
size_t  len 
) [static]

Definition at line 383 of file func_odbc.c.

References AST_APP_ARG, ast_atomic_fetchadd_int(), ast_autoservice_start(), ast_autoservice_stop(), ast_calloc, ast_channel_alloc(), ast_channel_datastore_add(), ast_channel_free(), ast_copy_string(), ast_datastore_alloc(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, ast_log(), ast_odbc_ast_str_SQLGetData(), ast_odbc_direct_execute(), ast_odbc_release_obj(), ast_odbc_request_obj(), ast_realloc, AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_append(), ast_str_append_escapecommas(), ast_str_buffer(), ast_str_make_space(), ast_str_reset(), ast_str_strlen(), ast_str_substitute_variables(), ast_str_thread_get(), ast_strlen_zero(), ast_test_flag, ast_verb, ast_datastore::data, dsn, generic_execute(), odbc_datastore_row::list, LOG_ERROR, LOG_WARNING, odbc_datastore_free(), OPT_ESCAPECOMMAS, OPT_MULTIROW, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), resultcount, and status.

Referenced by init_acf_query().

{
   struct odbc_obj *obj = NULL;
   struct acf_odbc_query *query;
   char varname[15], rowcount[12] = "-1";
   struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
   int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(field)[100];
   );
   SQLHSTMT stmt = NULL;
   SQLSMALLINT colcount=0;
   SQLLEN indicator;
   SQLSMALLINT collength;
   struct odbc_datastore *resultset = NULL;
   struct odbc_datastore_row *row = NULL;
   struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
   const char *status = "FAILURE";

   if (!sql || !colnames) {
      pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
      return -1;
   }

   ast_str_reset(colnames);

   AST_RWLIST_RDLOCK(&queries);
   AST_RWLIST_TRAVERSE(&queries, query, list) {
      if (!strcmp(query->acf->name, cmd)) {
         break;
      }
   }

   if (!query) {
      ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
      AST_RWLIST_UNLOCK(&queries);
      pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
      pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
      return -1;
   }

   if (!chan) {
      if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc"))) {
         bogus_chan = 1;
      }
   }

   if (chan) {
      ast_autoservice_start(chan);
   }

   AST_STANDARD_APP_ARGS(args, s);
   for (x = 0; x < args.argc; x++) {
      snprintf(varname, sizeof(varname), "ARG%d", x + 1);
      pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
   }

   ast_str_substitute_variables(&sql, 0, chan, query->sql_read);

   /* Restore prior values */
   for (x = 0; x < args.argc; x++) {
      snprintf(varname, sizeof(varname), "ARG%d", x + 1);
      pbx_builtin_setvar_helper(chan, varname, NULL);
   }

   /* Save these flags, so we can release the lock */
   escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
   if (ast_test_flag(query, OPT_MULTIROW)) {
      resultset = ast_calloc(1, sizeof(*resultset));
      AST_LIST_HEAD_INIT(resultset);
      if (query->rowlimit) {
         rowlimit = query->rowlimit;
      } else {
         rowlimit = INT_MAX;
      }
   }
   AST_RWLIST_UNLOCK(&queries);

   for (dsn = 0; dsn < 5; dsn++) {
      if (!ast_strlen_zero(query->readhandle[dsn])) {
         obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
         if (obj) {
            stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql));
         }
      }
      if (stmt) {
         break;
      }
      if (obj) {
         ast_odbc_release_obj(obj);
         obj = NULL;
      }
   }

   if (!stmt) {
      ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
      if (obj) {
         ast_odbc_release_obj(obj);
         obj = NULL;
      }
      pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
      if (chan) {
         ast_autoservice_stop(chan);
      }
      if (bogus_chan) {
         ast_channel_free(chan);
      }
      return -1;
   }

   res = SQLNumResultCols(stmt, &colcount);
   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
      ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
      SQLCloseCursor(stmt);
      SQLFreeHandle (SQL_HANDLE_STMT, stmt);
      ast_odbc_release_obj(obj);
      obj = NULL;
      pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
      if (chan) {
         ast_autoservice_stop(chan);
      }
      if (bogus_chan) {
         ast_channel_free(chan);
      }
      return -1;
   }

   res = SQLFetch(stmt);
   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
      int res1 = -1;
      if (res == SQL_NO_DATA) {
         ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
         res1 = 0;
         buf[0] = '\0';
         ast_copy_string(rowcount, "0", sizeof(rowcount));
         status = "NODATA";
      } else {
         ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
         status = "FETCHERROR";
      }
      SQLCloseCursor(stmt);
      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
      ast_odbc_release_obj(obj);
      obj = NULL;
      pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
      pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
      if (chan)
         ast_autoservice_stop(chan);
      if (bogus_chan)
         ast_channel_free(chan);
      return res1;
   }

   status = "SUCCESS";

   for (y = 0; y < rowlimit; y++) {
      buf[0] = '\0';
      for (x = 0; x < colcount; x++) {
         int i;
         struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
         char *ptrcoldata;

         if (!coldata) {
            ast_free(resultset);
            SQLCloseCursor(stmt);
            SQLFreeHandle(SQL_HANDLE_STMT, stmt);
            ast_odbc_release_obj(obj);
            obj = NULL;
            pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
            if (chan)
               ast_autoservice_stop(chan);
            if (bogus_chan) {
               ast_channel_free(chan);
            }
            return -1;
         }

         if (y == 0) {
            char colname[256];
            SQLULEN maxcol;

            res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
            ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
            if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
               snprintf(colname, sizeof(colname), "field%d", x);
            }

            ast_str_make_space(&coldata, maxcol + 1);

            if (ast_str_strlen(colnames)) {
               ast_str_append(&colnames, 0, ",");
            }
            ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));

            if (resultset) {
               void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
               if (!tmp) {
                  ast_log(LOG_ERROR, "No space for a new resultset?\n");
                  ast_free(resultset);
                  SQLCloseCursor(stmt);
                  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                  ast_odbc_release_obj(obj);
                  obj = NULL;
                  pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
                  pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
                  if (chan)
                     ast_autoservice_stop(chan);
                  if (bogus_chan)
                     ast_channel_free(chan);
                  return -1;
               }
               resultset = tmp;
               strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
            }
         }

         buflen = strlen(buf);
         res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
         if (indicator == SQL_NULL_DATA) {
            ast_debug(3, "Got NULL data\n");
            ast_str_reset(coldata);
            res = SQL_SUCCESS;
         }

         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
            ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
            y = -1;
            buf[0] = '\0';
            goto end_acf_read;
         }

         ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));

         if (x) {
            buf[buflen++] = ',';
         }

         /* Copy data, encoding '\' and ',' for the argument parser */
         ptrcoldata = ast_str_buffer(coldata);
         for (i = 0; i < ast_str_strlen(coldata); i++) {
            if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
               buf[buflen++] = '\\';
            }
            buf[buflen++] = ptrcoldata[i];

            if (buflen >= len - 2) {
               break;
            }

            if (ptrcoldata[i] == '\0') {
               break;
            }
         }

         buf[buflen] = '\0';
         ast_debug(2, "buf is now set to '%s'\n", buf);
      }
      ast_debug(2, "buf is now set to '%s'\n", buf);

      if (resultset) {
         row = ast_calloc(1, sizeof(*row) + buflen + 1);
         if (!row) {
            ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
            status = "MEMERROR";
            goto end_acf_read;
         }
         strcpy((char *)row + sizeof(*row), buf);
         AST_LIST_INSERT_TAIL(resultset, row, list);

         /* Get next row */
         res = SQLFetch(stmt);
         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
            if (res != SQL_NO_DATA) {
               ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
            }
            /* Number of rows in the resultset */
            y++;
            break;
         }
      }
   }

end_acf_read:
   snprintf(rowcount, sizeof(rowcount), "%d", y);
   pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
   pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
   pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
   if (resultset) {
      int uid;
      struct ast_datastore *odbc_store;
      uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
      snprintf(buf, len, "%d", uid);
      odbc_store = ast_datastore_alloc(&odbc_info, buf);
      if (!odbc_store) {
         ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel.  Results fail.\n");
         pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
         odbc_datastore_free(resultset);
         SQLCloseCursor(stmt);
         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
         ast_odbc_release_obj(obj);
         obj = NULL;
         if (chan)
            ast_autoservice_stop(chan);
         if (bogus_chan)
            ast_channel_free(chan);
         return -1;
      }
      odbc_store->data = resultset;
      ast_channel_datastore_add(chan, odbc_store);
   }
   SQLCloseCursor(stmt);
   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
   ast_odbc_release_obj(obj);
   obj = NULL;
   if (chan)
      ast_autoservice_stop(chan);
   if (bogus_chan)
      ast_channel_free(chan);
   return 0;
}
static int acf_odbc_write ( struct ast_channel chan,
const char *  cmd,
char *  s,
const char *  value 
) [static]

Note:
Okay, this part is confusing. Transactions belong to a single database handle. Therefore, when working with transactions, we CANNOT failover to multiple DSNs. We MUST have a single handle all the way through the transaction, or else we CANNOT enforce atomicity.

Definition at line 203 of file func_odbc.c.

References AST_APP_ARG, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_alloc(), ast_channel_free(), AST_DECLARE_APP_ARGS, ast_log(), ast_odbc_direct_execute(), ast_odbc_release_obj(), ast_odbc_request_obj(), ast_odbc_retrieve_transaction_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_buffer(), ast_str_make_space(), ast_str_strlen(), ast_str_substitute_variables(), ast_str_thread_get(), ast_strdupa, ast_strlen_zero(), buf, dsn, generic_execute(), odbc_datastore_row::list, LOG_ERROR, LOG_WARNING, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), and status.

Referenced by init_acf_query().

{
   struct odbc_obj *obj = NULL;
   struct acf_odbc_query *query;
   char *t, varname[15];
   int i, dsn, bogus_chan = 0;
   int transactional = 0;
   AST_DECLARE_APP_ARGS(values,
      AST_APP_ARG(field)[100];
   );
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(field)[100];
   );
   SQLHSTMT stmt = NULL;
   SQLLEN rows=0;
   struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
   struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
   const char *status = "FAILURE";

   if (!buf || !insertbuf) {
      return -1;
   }

   AST_RWLIST_RDLOCK(&queries);
   AST_RWLIST_TRAVERSE(&queries, query, list) {
      if (!strcmp(query->acf->name, cmd)) {
         break;
      }
   }

   if (!query) {
      ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
      AST_RWLIST_UNLOCK(&queries);
      pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
      return -1;
   }

   if (!chan) {
      if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
         bogus_chan = 1;
   }

   if (chan)
      ast_autoservice_start(chan);

   ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
   ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);

   /* Parse our arguments */
   t = value ? ast_strdupa(value) : "";

   if (!s || !t) {
      ast_log(LOG_ERROR, "Out of memory\n");
      AST_RWLIST_UNLOCK(&queries);
      if (chan)
         ast_autoservice_stop(chan);
      if (bogus_chan) {
         ast_channel_free(chan);
      } else {
         pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
      }
      return -1;
   }

   AST_STANDARD_APP_ARGS(args, s);
   for (i = 0; i < args.argc; i++) {
      snprintf(varname, sizeof(varname), "ARG%d", i + 1);
      pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
   }

   /* Parse values, just like arguments */
   AST_STANDARD_APP_ARGS(values, t);
   for (i = 0; i < values.argc; i++) {
      snprintf(varname, sizeof(varname), "VAL%d", i + 1);
      pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
   }

   /* Additionally set the value as a whole (but push an empty string if value is NULL) */
   pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");

   ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
   ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);

   /* Restore prior values */
   for (i = 0; i < args.argc; i++) {
      snprintf(varname, sizeof(varname), "ARG%d", i + 1);
      pbx_builtin_setvar_helper(chan, varname, NULL);
   }

   for (i = 0; i < values.argc; i++) {
      snprintf(varname, sizeof(varname), "VAL%d", i + 1);
      pbx_builtin_setvar_helper(chan, varname, NULL);
   }
   pbx_builtin_setvar_helper(chan, "VALUE", NULL);

   /*!\note
    * Okay, this part is confusing.  Transactions belong to a single database
    * handle.  Therefore, when working with transactions, we CANNOT failover
    * to multiple DSNs.  We MUST have a single handle all the way through the
    * transaction, or else we CANNOT enforce atomicity.
    */
   for (dsn = 0; dsn < 5; dsn++) {
      if (transactional) {
         /* This can only happen second time through or greater. */
         ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
      }

      if (!ast_strlen_zero(query->writehandle[dsn])) {
         if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
            transactional = 1;
         } else {
            obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
            transactional = 0;
         }
         if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
            break;
         }
      }

      if (obj && !transactional) {
         ast_odbc_release_obj(obj);
         obj = NULL;
      }
   }

   if (stmt) {
      SQLRowCount(stmt, &rows);
   }

   if (stmt && rows == 0 && ast_str_strlen(insertbuf) != 0) {
      SQLCloseCursor(stmt);
      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
      for (dsn = 0; dsn < 5; dsn++) {
         if (!ast_strlen_zero(query->writehandle[dsn])) {
            obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
            if (obj) {
               stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
            }
         }
         if (stmt) {
            status = "FAILOVER";
            SQLRowCount(stmt, &rows);
            break;
         }
         if (obj) {
            ast_odbc_release_obj(obj);
            obj = NULL;
         }
      }
   } else if (stmt) {
      status = "SUCCESS";
   }

   AST_RWLIST_UNLOCK(&queries);

   /* Output the affected rows, for all cases.  In the event of failure, we
    * flag this as -1 rows.  Note that this is different from 0 affected rows
    * which would be the case if we succeeded in our query, but the values did
    * not change. */
   snprintf(varname, sizeof(varname), "%d", (int)rows);
   pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
   pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);

   if (stmt) {
      SQLCloseCursor(stmt);
      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
   }
   if (obj && !transactional) {
      ast_odbc_release_obj(obj);
      obj = NULL;
   }

   if (chan)
      ast_autoservice_stop(chan);
   if (bogus_chan)
      ast_channel_free(chan);

   return 0;
}
AST_THREADSTORAGE_CUSTOM_SCOPE ( sql_buf  ,
NULL  ,
ast_free_ptr  ,
static   
)
AST_THREADSTORAGE_CUSTOM_SCOPE ( sql2_buf  ,
NULL  ,
ast_free_ptr  ,
static   
)
AST_THREADSTORAGE_CUSTOM_SCOPE ( coldata_buf  ,
NULL  ,
ast_free_ptr  ,
static   
)
AST_THREADSTORAGE_CUSTOM_SCOPE ( colnames_buf  ,
NULL  ,
ast_free_ptr  ,
static   
)
static char* cli_odbc_read ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1005 of file func_odbc.c.

References ast_cli_args::argc, ast_cli_args::argv, AST_APP_ARG, ast_channel_alloc(), ast_channel_free(), ast_cli(), ast_debug, AST_DECLARE_APP_ARGS, ast_odbc_ast_str_SQLGetData(), ast_odbc_direct_execute(), ast_odbc_release_obj(), ast_odbc_request_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_buffer(), ast_str_make_space(), ast_str_set(), ast_str_substitute_variables(), ast_str_thread_get(), ast_strdup, ast_strdupa, ast_strlen_zero(), chan, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, dsn, ast_cli_args::fd, generic_execute(), odbc_datastore_row::list, ast_cli_args::n, pbx_builtin_pushvar_helper(), ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

{
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(field)[100];
   );
   struct ast_str *sql;
   char *char_args, varname[10];
   struct acf_odbc_query *query;
   struct ast_channel *chan;
   int i;

   switch (cmd) {
   case CLI_INIT:
      e->command = "odbc read";
      e->usage =
         "Usage: odbc read <name> <args> [exec]\n"
         "       Evaluates the SQL provided in the ODBC function <name>, and\n"
         "       optionally executes the function.  This function is intended for\n"
         "       testing purposes.  Remember to quote arguments containing spaces.\n";
      return NULL;
   case CLI_GENERATE:
      if (a->pos == 2) {
         int wordlen = strlen(a->word), which = 0;
         /* Complete function name */
         AST_RWLIST_RDLOCK(&queries);
         AST_RWLIST_TRAVERSE(&queries, query, list) {
            if (!strncasecmp(query->acf->name, a->word, wordlen)) {
               if (++which > a->n) {
                  char *res = ast_strdup(query->acf->name);
                  AST_RWLIST_UNLOCK(&queries);
                  return res;
               }
            }
         }
         AST_RWLIST_UNLOCK(&queries);
         return NULL;
      } else if (a->pos == 4) {
         return a->n == 0 ? ast_strdup("exec") : NULL;
      } else {
         return NULL;
      }
   }

   if (a->argc < 4 || a->argc > 5) {
      return CLI_SHOWUSAGE;
   }

   sql = ast_str_thread_get(&sql_buf, 16);
   if (!sql) {
      return CLI_FAILURE;
   }

   AST_RWLIST_RDLOCK(&queries);
   AST_RWLIST_TRAVERSE(&queries, query, list) {
      if (!strcmp(query->acf->name, a->argv[2])) {
         break;
      }
   }

   if (!query) {
      ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
      AST_RWLIST_UNLOCK(&queries);
      return CLI_SHOWUSAGE;
   }

   if (ast_strlen_zero(query->sql_read)) {
      ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
      AST_RWLIST_UNLOCK(&queries);
      return CLI_SUCCESS;
   }

   ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);

   /* Evaluate function */
   char_args = ast_strdupa(a->argv[3]);

   chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc");

   AST_STANDARD_APP_ARGS(args, char_args);
   for (i = 0; i < args.argc; i++) {
      snprintf(varname, sizeof(varname), "ARG%d", i + 1);
      pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
   }

   ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
   ast_channel_free(chan);

   if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
      /* Execute the query */
      struct odbc_obj *obj = NULL;
      int dsn, executed = 0;
      SQLHSTMT stmt;
      int rows = 0, res, x;
      SQLSMALLINT colcount = 0, collength;
      SQLLEN indicator;
      struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
      char colname[256];
      SQLULEN maxcol;

      if (!coldata) {
         AST_RWLIST_UNLOCK(&queries);
         return CLI_SUCCESS;
      }

      for (dsn = 0; dsn < 5; dsn++) {
         if (ast_strlen_zero(query->readhandle[dsn])) {
            continue;
         }
         ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
         if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) {
            continue;
         }

         ast_debug(1, "Got obj\n");
         if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
            ast_odbc_release_obj(obj);
            obj = NULL;
            continue;
         }

         executed = 1;

         res = SQLNumResultCols(stmt, &colcount);
         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
            ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
            SQLCloseCursor(stmt);
            SQLFreeHandle (SQL_HANDLE_STMT, stmt);
            ast_odbc_release_obj(obj);
            obj = NULL;
            AST_RWLIST_UNLOCK(&queries);
            return CLI_SUCCESS;
         }

         res = SQLFetch(stmt);
         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
            SQLCloseCursor(stmt);
            SQLFreeHandle(SQL_HANDLE_STMT, stmt);
            ast_odbc_release_obj(obj);
            obj = NULL;
            if (res == SQL_NO_DATA) {
               ast_cli(a->fd, "Returned %d rows.  Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql));
               break;
            } else {
               ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
            }
            AST_RWLIST_UNLOCK(&queries);
            return CLI_SUCCESS;
         }
         for (;;) {
            for (x = 0; x < colcount; x++) {
               res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
               if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
                  snprintf(colname, sizeof(colname), "field%d", x);
               }

               res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
               if (indicator == SQL_NULL_DATA) {
                  ast_str_set(&coldata, 0, "(nil)");
                  res = SQL_SUCCESS;
               }

               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                  ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
                  SQLCloseCursor(stmt);
                  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                  ast_odbc_release_obj(obj);
                  obj = NULL;
                  AST_RWLIST_UNLOCK(&queries);
                  return CLI_SUCCESS;
               }

               ast_cli(a->fd, "%-20.20s  %s\n", colname, ast_str_buffer(coldata));
            }
            rows++;

            /* Get next row */
            res = SQLFetch(stmt);
            if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
               break;
            }
            ast_cli(a->fd, "%-20.20s  %s\n", "----------", "----------");
         }
         SQLCloseCursor(stmt);
         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
         ast_odbc_release_obj(obj);
         obj = NULL;
         ast_cli(a->fd, "Returned %d row%s.  Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
         break;
      }
      if (obj) {
         ast_odbc_release_obj(obj);
         obj = NULL;
      }

      if (!executed) {
         ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
      }
   } else { /* No execution, just print out the resulting SQL */
      ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
   }
   AST_RWLIST_UNLOCK(&queries);
   return CLI_SUCCESS;
}
static char* cli_odbc_write ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1209 of file func_odbc.c.

References ast_cli_args::argc, ast_cli_args::argv, AST_APP_ARG, ast_channel_alloc(), ast_channel_free(), ast_cli(), ast_debug, AST_DECLARE_APP_ARGS, ast_odbc_direct_execute(), ast_odbc_release_obj(), ast_odbc_request_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_buffer(), ast_str_make_space(), ast_str_substitute_variables(), ast_str_thread_get(), ast_strdup, ast_strdupa, ast_strlen_zero(), chan, CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, dsn, ast_cli_args::fd, generic_execute(), odbc_datastore_row::list, ast_cli_args::n, pbx_builtin_pushvar_helper(), ast_cli_args::pos, S_OR, ast_cli_entry::usage, and ast_cli_args::word.

{
   AST_DECLARE_APP_ARGS(values,
      AST_APP_ARG(field)[100];
   );
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(field)[100];
   );
   struct ast_str *sql;
   char *char_args, *char_values, varname[10];
   struct acf_odbc_query *query;
   struct ast_channel *chan;
   int i;

   switch (cmd) {
   case CLI_INIT:
      e->command = "odbc write";
      e->usage =
         "Usage: odbc write <name> <args> <value> [exec]\n"
         "       Evaluates the SQL provided in the ODBC function <name>, and\n"
         "       optionally executes the function.  This function is intended for\n"
         "       testing purposes.  Remember to quote arguments containing spaces.\n";
      return NULL;
   case CLI_GENERATE:
      if (a->pos == 2) {
         int wordlen = strlen(a->word), which = 0;
         /* Complete function name */
         AST_RWLIST_RDLOCK(&queries);
         AST_RWLIST_TRAVERSE(&queries, query, list) {
            if (!strncasecmp(query->acf->name, a->word, wordlen)) {
               if (++which > a->n) {
                  char *res = ast_strdup(query->acf->name);
                  AST_RWLIST_UNLOCK(&queries);
                  return res;
               }
            }
         }
         AST_RWLIST_UNLOCK(&queries);
         return NULL;
      } else if (a->pos == 5) {
         return a->n == 0 ? ast_strdup("exec") : NULL;
      } else {
         return NULL;
      }
   }

   if (a->argc < 5 || a->argc > 6) {
      return CLI_SHOWUSAGE;
   }

   sql = ast_str_thread_get(&sql_buf, 16);
   if (!sql) {
      return CLI_FAILURE;
   }

   AST_RWLIST_RDLOCK(&queries);
   AST_RWLIST_TRAVERSE(&queries, query, list) {
      if (!strcmp(query->acf->name, a->argv[2])) {
         break;
      }
   }

   if (!query) {
      ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
      AST_RWLIST_UNLOCK(&queries);
      return CLI_SHOWUSAGE;
   }

   if (ast_strlen_zero(query->sql_write)) {
      ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
      AST_RWLIST_UNLOCK(&queries);
      return CLI_SUCCESS;
   }

   ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);

   /* Evaluate function */
   char_args = ast_strdupa(a->argv[3]);
   char_values = ast_strdupa(a->argv[4]);

   chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc");

   AST_STANDARD_APP_ARGS(args, char_args);
   for (i = 0; i < args.argc; i++) {
      snprintf(varname, sizeof(varname), "ARG%d", i + 1);
      pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
   }

   /* Parse values, just like arguments */
   AST_STANDARD_APP_ARGS(values, char_values);
   for (i = 0; i < values.argc; i++) {
      snprintf(varname, sizeof(varname), "VAL%d", i + 1);
      pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
   }

   /* Additionally set the value as a whole (but push an empty string if value is NULL) */
   pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
   ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
   ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
   ast_channel_free(chan);

   if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
      /* Execute the query */
      struct odbc_obj *obj = NULL;
      int dsn, executed = 0;
      SQLHSTMT stmt;
      SQLLEN rows = -1;

      for (dsn = 0; dsn < 5; dsn++) {
         if (ast_strlen_zero(query->writehandle[dsn])) {
            continue;
         }
         if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) {
            continue;
         }
         if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
            ast_odbc_release_obj(obj);
            obj = NULL;
            continue;
         }

         SQLRowCount(stmt, &rows);
         SQLCloseCursor(stmt);
         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
         ast_odbc_release_obj(obj);
         obj = NULL;
         ast_cli(a->fd, "Affected %d rows.  Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]);
         executed = 1;
         break;
      }

      if (!executed) {
         ast_cli(a->fd, "Failed to execute query.\n");
      }
   } else { /* No execution, just print out the resulting SQL */
      ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
   }
   AST_RWLIST_UNLOCK(&queries);
   return CLI_SUCCESS;
}
static int exec_odbcfinish ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 762 of file func_odbc.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), and ast_datastore_free().

Referenced by load_module().

{
   struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
   if (!store) /* Already freed; no big deal. */
      return 0;
   ast_channel_datastore_remove(chan, store);
   ast_datastore_free(store);
   return 0;
}
static int free_acf_query ( struct acf_odbc_query query) [static]

Definition at line 991 of file func_odbc.c.

References ast_free, and ast_string_field_free_memory.

Referenced by reload(), and unload_module().

{
   if (query) {
      if (query->acf) {
         if (query->acf->name)
            ast_free((char *)query->acf->name);
         ast_string_field_free_memory(query->acf);
         ast_free(query->acf);
      }
      ast_free(query);
   }
   return 0;
}
static SQLHSTMT generic_execute ( struct odbc_obj obj,
void *  data 
) [static]

Definition at line 160 of file func_odbc.c.

References ast_log(), odbc_obj::con, odbc_datastore_row::data, and LOG_WARNING.

Referenced by acf_odbc_read(), acf_odbc_write(), cli_odbc_read(), and cli_odbc_write().

{
   int res;
   char *sql = data;
   SQLHSTMT stmt;

   res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
      ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
      return NULL;
   }

   res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
   if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
      if (res == SQL_ERROR) {
         int i;
         SQLINTEGER nativeerror=0, numfields=0;
         SQLSMALLINT diagbytes=0;
         unsigned char state[10], diagnostic[256];

         SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
         for (i = 0; i < numfields; i++) {
            SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
            ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
            if (i > 10) {
               ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
               break;
            }
         }
      }

      ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
      SQLCloseCursor(stmt);
      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
      return NULL;
   }

   return stmt;
}
static int init_acf_query ( struct ast_config cfg,
char *  catg,
struct acf_odbc_query **  query 
) [static]

Definition at line 772 of file func_odbc.c.

References acf_odbc_read(), acf_odbc_write(), asprintf, AST_APP_ARG, ast_calloc, ast_clear_flag, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_false(), ast_free, ast_log(), ast_set_flag, AST_STANDARD_APP_ARGS, ast_strdupa, ast_string_field_build, ast_string_field_free_memory, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ast_variable_retrieve(), desc, dsn, errno, LOG_ERROR, LOG_WARNING, OPT_ESCAPECOMMAS, OPT_MULTIROW, and synopsis.

Referenced by load_module(), and reload().

{
   const char *tmp;
   int i;

   if (!cfg || !catg) {
      return EINVAL;
   }

   *query = ast_calloc(1, sizeof(struct acf_odbc_query));
   if (! (*query))
      return ENOMEM;

   if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
      char *tmp2 = ast_strdupa(tmp);
      AST_DECLARE_APP_ARGS(writeconf,
         AST_APP_ARG(dsn)[5];
      );
      AST_STANDARD_APP_ARGS(writeconf, tmp2);
      for (i = 0; i < 5; i++) {
         if (!ast_strlen_zero(writeconf.dsn[i]))
            ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
      }
   }

   if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
      char *tmp2 = ast_strdupa(tmp);
      AST_DECLARE_APP_ARGS(readconf,
         AST_APP_ARG(dsn)[5];
      );
      AST_STANDARD_APP_ARGS(readconf, tmp2);
      for (i = 0; i < 5; i++) {
         if (!ast_strlen_zero(readconf.dsn[i]))
            ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
      }
   } else {
      /* If no separate readhandle, then use the writehandle for reading */
      for (i = 0; i < 5; i++) {
         if (!ast_strlen_zero((*query)->writehandle[i]))
            ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
      }
   }

   if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
      ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
   else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
      ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
      ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
   }

   if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
      ast_free(*query);
      *query = NULL;
      ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
      return EINVAL;
   }

   if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
      ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
   else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
      ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
      ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
   }

   if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
      ast_free(*query);
      *query = NULL;
      ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
      return EINVAL;
   }

   if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
      ast_copy_string((*query)->sql_insert, tmp, sizeof((*query)->sql_insert));
   }

   /* Allow escaping of embedded commas in fields to be turned off */
   ast_set_flag((*query), OPT_ESCAPECOMMAS);
   if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
      if (ast_false(tmp))
         ast_clear_flag((*query), OPT_ESCAPECOMMAS);
   }

   if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
      if (strcasecmp(tmp, "multirow") == 0)
         ast_set_flag((*query), OPT_MULTIROW);
      if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
         sscanf(tmp, "%30d", &((*query)->rowlimit));
   }

   (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
   if (! (*query)->acf) {
      ast_free(*query);
      *query = NULL;
      return ENOMEM;
   }
   if (ast_string_field_init((*query)->acf, 128)) {
      ast_free((*query)->acf);
      ast_free(*query);
      *query = NULL;
      return ENOMEM;
   }

   if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
      if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
         ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
      }
   } else {
      if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
         ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
      }
   }

   if (!((*query)->acf->name)) {
      ast_string_field_free_memory((*query)->acf);
      ast_free((*query)->acf);
      ast_free(*query);
      *query = NULL;
      return ENOMEM;
   }

   if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
      ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
   } else {
      ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
   }

   if (ast_strlen_zero((*query)->acf->syntax)) {
      ast_free((char *)(*query)->acf->name);
      ast_string_field_free_memory((*query)->acf);
      ast_free((*query)->acf);
      ast_free(*query);
      *query = NULL;
      return ENOMEM;
   }

   if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
      ast_string_field_set((*query)->acf, synopsis, tmp);
   } else {
      ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
   }

   if (ast_strlen_zero((*query)->acf->synopsis)) {
      ast_free((char *)(*query)->acf->name);
      ast_string_field_free_memory((*query)->acf);
      ast_free((*query)->acf);
      ast_free(*query);
      *query = NULL;
      return ENOMEM;
   }

   if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
      ast_string_field_build((*query)->acf, desc,
               "Runs the following query, as defined in func_odbc.conf, performing\n"
                  "substitution of the arguments into the query as specified by ${ARG1},\n"
               "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
               "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
               "%s"
               "\nRead:\n%s\n\nWrite:\n%s\n%s%s%s",
               ast_strlen_zero((*query)->sql_insert) ? "" :
                  "If the write query affects no rows, the insert query will be\n"
                  "performed.\n",
               (*query)->sql_read,
               (*query)->sql_write,
               ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
               ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
               ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
   } else if (!ast_strlen_zero((*query)->sql_read)) {
      ast_string_field_build((*query)->acf, desc,
                  "Runs the following query, as defined in func_odbc.conf, performing\n"
                     "substitution of the arguments into the query as specified by ${ARG1},\n"
                  "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
                  (*query)->sql_read);
   } else if (!ast_strlen_zero((*query)->sql_write)) {
      ast_string_field_build((*query)->acf, desc,  
               "Runs the following query, as defined in func_odbc.conf, performing\n"
                  "substitution of the arguments into the query as specified by ${ARG1},\n"
               "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
               "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
               "This function may only be set.\n%sSQL:\n%s\n%s%s%s",
               ast_strlen_zero((*query)->sql_insert) ? "" :
                  "If the write query affects no rows, the insert query will be\n"
                  "performed.\n",
               (*query)->sql_write,
               ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
               ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
               ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
   } else {
      ast_string_field_free_memory((*query)->acf);
      ast_free((char *)(*query)->acf->name);
      ast_free((*query)->acf);
      ast_free(*query);
      ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute.  Ignoring.\n", catg);
      return EINVAL;
   }

   if (ast_strlen_zero((*query)->acf->desc)) {
      ast_string_field_free_memory((*query)->acf);
      ast_free((char *)(*query)->acf->name);
      ast_free((*query)->acf);
      ast_free(*query);
      *query = NULL;
      return ENOMEM;
   }

   if (ast_strlen_zero((*query)->sql_read)) {
      (*query)->acf->read = NULL;
   } else {
      (*query)->acf->read = acf_odbc_read;
   }

   if (ast_strlen_zero((*query)->sql_write)) {
      (*query)->acf->write = NULL;
   } else {
      (*query)->acf->write = acf_odbc_write;
   }

   return 0;
}
static int load_module ( void  ) [static]

Definition at line 1355 of file func_odbc.c.

References app_odbcfinish, ARRAY_LEN, ast_category_browse(), ast_cli_register_multiple(), ast_config_destroy(), ast_config_load, ast_custom_function_register, ast_log(), AST_MODULE_LOAD_DECLINE, ast_register_application_xml, AST_RWLIST_INSERT_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, cli_func_odbc, CONFIG_STATUS_FILEINVALID, escape_function, exec_odbcfinish(), fetch_function, init_acf_query(), odbc_datastore_row::list, LOG_ERROR, and LOG_NOTICE.

{
   int res = 0;
   struct ast_config *cfg;
   char *catg;
   struct ast_flags config_flags = { 0 };

   res |= ast_custom_function_register(&fetch_function);
   res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
   AST_RWLIST_WRLOCK(&queries);

   cfg = ast_config_load(config, config_flags);
   if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
      AST_RWLIST_UNLOCK(&queries);
      return AST_MODULE_LOAD_DECLINE;
   }

   for (catg = ast_category_browse(cfg, NULL);
        catg;
        catg = ast_category_browse(cfg, catg)) {
      struct acf_odbc_query *query = NULL;
      int err;

      if ((err = init_acf_query(cfg, catg, &query))) {
         if (err == ENOMEM)
            ast_log(LOG_ERROR, "Out of memory\n");
         else if (err == EINVAL)
            ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
         else
            ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
      } else {
         AST_RWLIST_INSERT_HEAD(&queries, query, list);
         ast_custom_function_register(query->acf);
      }
   }

   ast_config_destroy(cfg);
   res |= ast_custom_function_register(&escape_function);
   ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));

   AST_RWLIST_UNLOCK(&queries);
   return res;
}
static void odbc_datastore_free ( void *  data) [static]

Definition at line 147 of file func_odbc.c.

References ast_free, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, odbc_datastore_row::data, and odbc_datastore_row::list.

Referenced by acf_odbc_read().

{
   struct odbc_datastore *result = data;
   struct odbc_datastore_row *row;
   AST_LIST_LOCK(result);
   while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
      ast_free(row);
   }
   AST_LIST_UNLOCK(result);
   AST_LIST_HEAD_DESTROY(result);
   ast_free(result);
}
static int reload ( void  ) [static]

Definition at line 1426 of file func_odbc.c.

References ast_category_browse(), ast_config_destroy(), ast_config_load, ast_custom_function_register, ast_custom_function_unregister(), ast_log(), AST_RWLIST_EMPTY, AST_RWLIST_INSERT_HEAD, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, free_acf_query(), init_acf_query(), odbc_datastore_row::list, LOG_ERROR, and LOG_WARNING.

{
   int res = 0;
   struct ast_config *cfg;
   struct acf_odbc_query *oldquery;
   char *catg;
   struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };

   cfg = ast_config_load(config, config_flags);
   if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
      return 0;

   AST_RWLIST_WRLOCK(&queries);

   while (!AST_RWLIST_EMPTY(&queries)) {
      oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
      ast_custom_function_unregister(oldquery->acf);
      free_acf_query(oldquery);
   }

   if (!cfg) {
      ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
      goto reload_out;
   }

   for (catg = ast_category_browse(cfg, NULL);
        catg;
        catg = ast_category_browse(cfg, catg)) {
      struct acf_odbc_query *query = NULL;

      if (init_acf_query(cfg, catg, &query)) {
         ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
      } else {
         AST_RWLIST_INSERT_HEAD(&queries, query, list);
         ast_custom_function_register(query->acf);
      }
   }

   ast_config_destroy(cfg);
reload_out:
   AST_RWLIST_UNLOCK(&queries);
   return res;
}

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "ODBC lookups" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1476 of file func_odbc.c.

char* app_odbcfinish = "ODBCFinish" [static]

Definition at line 760 of file func_odbc.c.

Referenced by load_module(), and unload_module().

Definition at line 1476 of file func_odbc.c.

struct ast_cli_entry cli_func_odbc[] [static]
Initial value:
 {
   AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
   AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
}

Definition at line 1350 of file func_odbc.c.

Referenced by load_module(), and unload_module().

char* config = "func_odbc.conf" [static]

Definition at line 100 of file func_odbc.c.

Initial value:
 {
   .name = "SQL_ESC",
   .read = acf_escape,
   .write = NULL,
}

Definition at line 720 of file func_odbc.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   .name = "ODBC_FETCH",
   .read = acf_fetch,
   .write = NULL,
}

Definition at line 754 of file func_odbc.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   .type = "FUNC_ODBC",
   .destroy = odbc_datastore_free,
}

Definition at line 121 of file func_odbc.c.

enum { ... } odbc_option_flags
struct queries queries
int resultcount = 0 [static]

Definition at line 140 of file func_odbc.c.

Referenced by acf_odbc_read().