Mon Mar 12 2012 21:39:58

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  odbc_option_flags { OPT_ESCAPECOMMAS = (1 << 0), OPT_MULTIROW = (1 << 1) }

Functions

static void __init_coldata_buf (void)
static void __init_colnames_buf (void)
static void __init_sql2_buf (void)
static void __init_sql_buf (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)
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, const char *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 __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "ODBC lookups" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .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 struct ast_threadstorage coldata_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_coldata_buf , .custom_init = NULL , }
static struct ast_threadstorage colnames_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_colnames_buf , .custom_init = NULL , }
static char * config = "func_odbc.conf"
static struct ast_custom_function escape_function
static struct ast_custom_function fetch_function
static struct ast_datastore_info odbc_info
static struct queries queries
static int resultcount = 0
static struct ast_threadstorage sql2_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql2_buf , .custom_init = NULL , }
static struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql_buf , .custom_init = NULL , }

Detailed Description

ODBC lookups.

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

Definition in file func_odbc.c.


Enumeration Type Documentation

Enumerator:
OPT_ESCAPECOMMAS 
OPT_MULTIROW 

Definition at line 103 of file func_odbc.c.

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

Function Documentation

static void __init_coldata_buf ( void  ) [static]

Definition at line 145 of file func_odbc.c.

{
static void __init_colnames_buf ( void  ) [static]

Definition at line 146 of file func_odbc.c.

{
static void __init_sql2_buf ( void  ) [static]

Definition at line 144 of file func_odbc.c.

{
static void __init_sql_buf ( void  ) [static]

Definition at line 143 of file func_odbc.c.

{
static void __reg_module ( void  ) [static]

Definition at line 1550 of file func_odbc.c.

static void __unreg_module ( void  ) [static]

Definition at line 1550 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 767 of file func_odbc.c.

References 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 789 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, ast_datastore::data, odbc_datastore_row::data, odbc_datastore_row::list, odbc_datastore::names, and pbx_builtin_setvar_helper().

Referenced by acf_odbc_read().

{
   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 411 of file func_odbc.c.

References acf_fetch(), args, AST_APP_ARG, ast_atomic_fetchadd_int(), ast_autoservice_start(), ast_autoservice_stop(), ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_channel_unref, ast_copy_string(), ast_datastore_alloc(), ast_debug, AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), 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, coldata_buf, colnames_buf, 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, sql_buf, 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, multirow = 0, 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) {
      if (chan) {
         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);
      if (chan) {
         pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
         pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
      }
      return -1;
   }

   if (!chan) {
      if (!(chan = ast_dummy_channel_alloc())) {
         AST_RWLIST_UNLOCK(&queries);
         return -1;
      }
      bogus_chan = 1;
   }

   if (!bogus_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);

   if (bogus_chan) {
      chan = ast_channel_unref(chan);
   } else {
      /* 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 (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
      if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
         pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
         pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
         ast_autoservice_stop(chan);
         return -1;
      }
      AST_LIST_HEAD_INIT(resultset);
      if (query->rowlimit) {
         rowlimit = query->rowlimit;
      } else {
         rowlimit = INT_MAX;
      }
      multirow = 1;
   } else if (!bogus_chan) {
      if (query->rowlimit > 1) {
         rowlimit = query->rowlimit;
         if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
            pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
            pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
            ast_autoservice_stop(chan);
            return -1;
         }
         AST_LIST_HEAD_INIT(resultset);
      }
   }
   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;
      }
      if (!bogus_chan) {
         pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
         ast_autoservice_stop(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;
      if (!bogus_chan) {
         pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
         ast_autoservice_stop(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;
      if (!bogus_chan) {
         pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
         pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
         ast_autoservice_stop(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;
            if (!bogus_chan) {
               pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
               ast_autoservice_stop(chan);
            }
            return -1;
         }

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

            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;
                  if (!bogus_chan) {
                     pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
                     pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
                     ast_autoservice_stop(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:
   if (!bogus_chan) {
      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;
         if (multirow) {
            uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
            snprintf(buf, len, "%d", uid);
         } else {
            /* Name of the query is name of the resultset */
            ast_copy_string(buf, cmd, len);

            /* If there's one with the same name already, free it */
            ast_channel_lock(chan);
            if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) {
               ast_channel_datastore_remove(chan, odbc_store);
               odbc_datastore_free(odbc_store->data);
               ast_free(odbc_store);
            }
            ast_channel_unlock(chan);
         }
         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");
            odbc_datastore_free(resultset);
            SQLCloseCursor(stmt);
            SQLFreeHandle(SQL_HANDLE_STMT, stmt);
            ast_odbc_release_obj(obj);
            obj = NULL;
            pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
            ast_autoservice_stop(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 (resultset && !multirow) {
      /* Fetch the first resultset */
      if (!acf_fetch(chan, "", buf, buf, len)) {
         buf[0] = '\0';
      }
   }
   if (!bogus_chan) {
      ast_autoservice_stop(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 206 of file func_odbc.c.

References args, AST_APP_ARG, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_unref, AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), 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(), dsn, generic_execute(), odbc_datastore_row::list, LOG_ERROR, LOG_WARNING, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), sql2_buf, sql_buf, 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);
      if (chan) {
         pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
      }
      return -1;
   }

   if (!chan) {
      if (!(chan = ast_dummy_channel_alloc())) {
         AST_RWLIST_UNLOCK(&queries);
         return -1;
      }
      bogus_chan = 1;
   }

   if (!bogus_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 (!bogus_chan) {
         ast_autoservice_stop(chan);
         pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
      } else {
         ast_channel_unref(chan);
      }
      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);

   if (bogus_chan) {
      chan = ast_channel_unref(chan);
   } else {
      /* 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 (!ast_strlen_zero(query->writehandle[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 ((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);
      if (obj && !transactional) {
         ast_odbc_release_obj(obj);
         obj = NULL;
      }

      for (transactional = 0, dsn = 0; dsn < 5; dsn++) {
         if (!ast_strlen_zero(query->writehandle[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");
            } else if (obj) {
               ast_odbc_release_obj(obj);
               obj = NULL;
            }

            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(insertbuf));
            }
         }
         if (stmt) {
            status = "FAILOVER";
            SQLRowCount(stmt, &rows);
            break;
         }
      }
   } 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. */
   if (!bogus_chan) {
      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 (!bogus_chan) {
      ast_autoservice_stop(chan);
   }

   return 0;
}
static char* cli_odbc_read ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1068 of file func_odbc.c.

References ast_cli_args::argc, args, ast_cli_args::argv, AST_APP_ARG, ast_channel_unref, ast_cli(), ast_debug, AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), 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(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, coldata_buf, 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, sql_buf, 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 readsql 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_dummy_channel_alloc();
   if (!chan) {
      AST_RWLIST_UNLOCK(&queries);
      return CLI_FAILURE;
   }

   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);
   chan = ast_channel_unref(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++) {
               maxcol = 0;

               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 1278 of file func_odbc.c.

References ast_cli_args::argc, args, ast_cli_args::argv, AST_APP_ARG, ast_channel_unref, ast_cli(), ast_debug, AST_DECLARE_APP_ARGS, ast_dummy_channel_alloc(), 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(), 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, sql_buf, 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_dummy_channel_alloc();
   if (!chan) {
      AST_RWLIST_UNLOCK(&queries);
      return CLI_FAILURE;
   }

   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));

   chan = ast_channel_unref(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,
const char *  data 
) [static]

Definition at line 825 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 1054 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 163 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) && (res != SQL_NO_DATA)) {
      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 835 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 1429 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 150 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 1500 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 __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "ODBC lookups" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 1550 of file func_odbc.c.

char* app_odbcfinish = "ODBCFinish" [static]

Definition at line 823 of file func_odbc.c.

Referenced by load_module(), and unload_module().

Definition at line 1550 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 1424 of file func_odbc.c.

Referenced by load_module(), and unload_module().

struct ast_threadstorage coldata_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_coldata_buf , .custom_init = NULL , } [static]

Definition at line 145 of file func_odbc.c.

Referenced by acf_odbc_read(), and cli_odbc_read().

struct ast_threadstorage colnames_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_colnames_buf , .custom_init = NULL , } [static]

Definition at line 146 of file func_odbc.c.

Referenced by acf_odbc_read().

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

Definition at line 101 of file func_odbc.c.

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

Definition at line 783 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 817 of file func_odbc.c.

Referenced by load_module(), and unload_module().

struct ast_datastore_info odbc_info [static]
Initial value:
 {
   .type = "FUNC_ODBC",
   .destroy = odbc_datastore_free,
}

Definition at line 122 of file func_odbc.c.

struct queries queries [static]
int resultcount = 0 [static]

Definition at line 141 of file func_odbc.c.

Referenced by acf_odbc_read().

struct ast_threadstorage sql2_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql2_buf , .custom_init = NULL , } [static]

Definition at line 144 of file func_odbc.c.

Referenced by acf_odbc_write().

struct ast_threadstorage sql_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_sql_buf , .custom_init = NULL , } [static]

Definition at line 143 of file func_odbc.c.

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