Wed Aug 18 22:34:24 2010

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"

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 __init_coldata_buf (void)
static void __init_colnames_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 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 __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 = "a9c98e5d177805051735cb5b0b16b0a0" , .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_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 char * desc_odbcfinish
static struct ast_custom_function escape_function
static struct ast_custom_function fetch_function
ast_datastore_info odbc_info
enum { ... }  odbc_option_flags
static int resultcount = 0
static char * syn_odbcfinish = "Clear the resultset of a successful multirow query"


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

00049      {
00050    OPT_ESCAPECOMMAS =   (1 << 0),
00051    OPT_MULTIROW     =   (1 << 1),
00052 } odbc_option_flags;


Function Documentation

static void __init_coldata_buf ( void   )  [static]

Definition at line 88 of file func_odbc.c.

00092 {

static void __init_colnames_buf ( void   )  [static]

Definition at line 89 of file func_odbc.c.

00092 {

static void __reg_module ( void   )  [static]

Definition at line 960 of file func_odbc.c.

static void __unreg_module ( void   )  [static]

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

00555 {
00556    char *out = buf;
00557 
00558    for (; *data && out - buf < len; data++) {
00559       if (*data == '\'') {
00560          *out = '\'';
00561          out++;
00562       }
00563       *out++ = *data;
00564    }
00565    *out = '\0';
00566 
00567    return 0;
00568 }

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

Definition at line 582 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, chan, odbc_datastore_row::data, ast_datastore::data, odbc_datastore_row::list, odbc_datastore::names, odbc_info, and pbx_builtin_setvar_helper().

00583 {
00584    struct ast_datastore *store;
00585    struct odbc_datastore *resultset;
00586    struct odbc_datastore_row *row;
00587    store = ast_channel_datastore_find(chan, &odbc_info, data);
00588    if (!store) {
00589       return -1;
00590    }
00591    resultset = store->data;
00592    AST_LIST_LOCK(resultset);
00593    row = AST_LIST_REMOVE_HEAD(resultset, list);
00594    AST_LIST_UNLOCK(resultset);
00595    if (!row) {
00596       /* Cleanup datastore */
00597       ast_channel_datastore_remove(chan, store);
00598       ast_datastore_free(store);
00599       return -1;
00600    }
00601    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00602    ast_copy_string(buf, row->data, len);
00603    ast_free(row);
00604    return 0;
00605 }

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

Definition at line 260 of file func_odbc.c.

References acf_odbc_query::acf, AST_APP_ARG, ast_autoservice_start(), ast_calloc, ast_channel_alloc, AST_DECLARE_APP_ARGS, ast_free, AST_LIST_HEAD_INIT, ast_log(), ast_odbc_direct_execute(), ast_odbc_request_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_buffer, ast_str_create(), ast_str_make_space(), ast_str_reset(), ast_str_size, ast_str_strlen, ast_str_thread_get(), ast_str_update, ast_strlen_zero(), ast_test_flag, chan, colnames_buf, dsn, generic_execute(), acf_odbc_query::list, LOG_ERROR, ast_custom_function::name, OPT_ESCAPECOMMAS, OPT_MULTIROW, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), pbx_substitute_variables_helper(), acf_odbc_query::readhandle, acf_odbc_query::rowlimit, acf_odbc_query::sql_read, and ast_str::str.

Referenced by init_acf_query().

00261 {
00262    struct odbc_obj *obj = NULL;
00263    struct acf_odbc_query *query;
00264    char varname[15], rowcount[12] = "-1";
00265    struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
00266    int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
00267    AST_DECLARE_APP_ARGS(args,
00268       AST_APP_ARG(field)[100];
00269    );
00270    SQLHSTMT stmt = NULL;
00271    SQLSMALLINT colcount=0;
00272    SQLLEN indicator;
00273    SQLSMALLINT collength;
00274    struct odbc_datastore *resultset = NULL;
00275    struct odbc_datastore_row *row = NULL;
00276    struct ast_str *sql = ast_str_create(16);
00277 
00278    if (!sql) {
00279       return -1;
00280    }
00281 
00282    ast_str_reset(colnames);
00283 
00284    AST_RWLIST_RDLOCK(&queries);
00285    AST_RWLIST_TRAVERSE(&queries, query, list) {
00286       if (!strcmp(query->acf->name, cmd)) {
00287          break;
00288       }
00289    }
00290 
00291    if (!query) {
00292       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00293       AST_RWLIST_UNLOCK(&queries);
00294       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00295       ast_free(sql);
00296       return -1;
00297    }
00298 
00299    if (!chan) {
00300       if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00301          bogus_chan = 1;
00302    }
00303 
00304    if (chan)
00305       ast_autoservice_start(chan);
00306 
00307    AST_STANDARD_APP_ARGS(args, s);
00308    for (x = 0; x < args.argc; x++) {
00309       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00310       pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00311    }
00312 
00313    do {
00314       ast_str_make_space(&sql, 2 * (strlen(query->sql_read) > ast_str_size(sql) ? strlen(query->sql_read) : ast_str_size(sql)));
00315       pbx_substitute_variables_helper(chan, query->sql_read, ast_str_buffer(sql), ast_str_size(sql) - 1);
00316       ast_str_update(sql);
00317    } while (ast_str_strlen(sql) > ast_str_size(sql) - 5);
00318 
00319    /* Restore prior values */
00320    for (x = 0; x < args.argc; x++) {
00321       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00322       pbx_builtin_setvar_helper(chan, varname, NULL);
00323    }
00324 
00325    /* Save these flags, so we can release the lock */
00326    escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00327    if (ast_test_flag(query, OPT_MULTIROW)) {
00328       resultset = ast_calloc(1, sizeof(*resultset));
00329       AST_LIST_HEAD_INIT(resultset);
00330       if (query->rowlimit)
00331          rowlimit = query->rowlimit;
00332       else
00333          rowlimit = INT_MAX;
00334    }
00335    AST_RWLIST_UNLOCK(&queries);
00336 
00337    for (dsn = 0; dsn < 5; dsn++) {
00338       if (!ast_strlen_zero(query->readhandle[dsn])) {
00339          obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
00340          if (obj)
00341             stmt = ast_odbc_direct_execute(obj, generic_execute, sql->str);
00342       }
00343       if (stmt)
00344          break;
00345    }
00346 
00347    if (!stmt) {
00348       ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql->str);
00349       if (obj)
00350          ast_odbc_release_obj(obj);
00351       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00352       if (chan)
00353          ast_autoservice_stop(chan);
00354       if (bogus_chan)
00355          ast_channel_free(chan);
00356       ast_free(sql);
00357       return -1;
00358    }
00359 
00360    res = SQLNumResultCols(stmt, &colcount);
00361    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00362       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql->str);
00363       SQLCloseCursor(stmt);
00364       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00365       ast_odbc_release_obj(obj);
00366       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00367       if (chan)
00368          ast_autoservice_stop(chan);
00369       if (bogus_chan)
00370          ast_channel_free(chan);
00371       ast_free(sql);
00372       return -1;
00373    }
00374 
00375    res = SQLFetch(stmt);
00376    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00377       int res1 = -1;
00378       if (res == SQL_NO_DATA) {
00379          ast_verb(4, "Found no rows [%s]\n", sql->str);
00380          res1 = 0;
00381          buf[0] = '\0';
00382          ast_copy_string(rowcount, "0", sizeof(rowcount));
00383       } else {
00384          ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
00385       }
00386       SQLCloseCursor(stmt);
00387       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00388       ast_odbc_release_obj(obj);
00389       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00390       if (chan)
00391          ast_autoservice_stop(chan);
00392       if (bogus_chan)
00393          ast_channel_free(chan);
00394       ast_free(sql);
00395       return res1;
00396    }
00397 
00398    for (y = 0; y < rowlimit; y++) {
00399       buf[0] = '\0';
00400       for (x = 0; x < colcount; x++) {
00401          int i;
00402          struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00403 
00404          if (y == 0) {
00405             char colname[256];
00406             SQLULEN maxcol;
00407 
00408             res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00409             ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00410             if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00411                snprintf(colname, sizeof(colname), "field%d", x);
00412             }
00413 
00414             if (coldata->len < maxcol + 1) {
00415                ast_str_make_space(&coldata, maxcol + 1);
00416             }
00417 
00418             if (colnames->used) {
00419                ast_str_append(&colnames, 0, ",");
00420             }
00421             ast_str_make_space(&colnames, strlen(colname) * 2 + 1 + colnames->used);
00422 
00423             /* Copy data, encoding '\' and ',' for the argument parser */
00424             for (i = 0; i < sizeof(colname); i++) {
00425                if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
00426                   colnames->str[colnames->used++] = '\\';
00427                }
00428                colnames->str[colnames->used++] = colname[i];
00429 
00430                if (colname[i] == '\0') {
00431                   colnames->used--;
00432                   break;
00433                }
00434             }
00435 
00436             if (resultset) {
00437                void *tmp = ast_realloc(resultset, sizeof(*resultset) + colnames->used + 1);
00438                if (!tmp) {
00439                   ast_log(LOG_ERROR, "No space for a new resultset?\n");
00440                   ast_free(resultset);
00441                   SQLCloseCursor(stmt);
00442                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00443                   ast_odbc_release_obj(obj);
00444                   pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00445                   if (chan)
00446                      ast_autoservice_stop(chan);
00447                   if (bogus_chan)
00448                      ast_channel_free(chan);
00449                   ast_free(sql);
00450                   return -1;
00451                }
00452                resultset = tmp;
00453                strcpy((char *)resultset + sizeof(*resultset), colnames->str);
00454             }
00455          }
00456 
00457          buflen = strlen(buf);
00458          res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata->str, coldata->len, &indicator);
00459          if (indicator == SQL_NULL_DATA) {
00460             ast_debug(3, "Got NULL data\n");
00461             ast_str_reset(coldata);
00462             res = SQL_SUCCESS;
00463          }
00464 
00465          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00466             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql->str);
00467             y = -1;
00468             buf[0] = '\0';
00469             goto end_acf_read;
00470          }
00471 
00472          ast_debug(2, "Got coldata of '%s'\n", coldata->str);
00473          coldata->used = strlen(coldata->str);
00474 
00475          /* Copy data, encoding '\' and ',' for the argument parser */
00476          for (i = 0; i < coldata->used; i++) {
00477             if (escapecommas && (coldata->str[i] == '\\' || coldata->str[i] == ',')) {
00478                buf[buflen++] = '\\';
00479             }
00480             buf[buflen++] = coldata->str[i];
00481 
00482             if (buflen >= len - 2)
00483                break;
00484 
00485             if (coldata->str[i] == '\0')
00486                break;
00487          }
00488 
00489          buf[buflen++] = ',';
00490          buf[buflen] = '\0';
00491          ast_debug(2, "buf is now set to '%s'\n", buf);
00492       }
00493       /* Trim trailing comma */
00494       buf[buflen - 1] = '\0';
00495       ast_debug(2, "buf is now set to '%s'\n", buf);
00496 
00497       if (resultset) {
00498          row = ast_calloc(1, sizeof(*row) + buflen);
00499          if (!row) {
00500             ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00501             goto end_acf_read;
00502          }
00503          strcpy((char *)row + sizeof(*row), buf);
00504          AST_LIST_INSERT_TAIL(resultset, row, list);
00505 
00506          /* Get next row */
00507          res = SQLFetch(stmt);
00508          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00509             if (res != SQL_NO_DATA)
00510                ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
00511             y++;
00512             break;
00513          }
00514       }
00515    }
00516 
00517 end_acf_read:
00518    snprintf(rowcount, sizeof(rowcount), "%d", y);
00519    pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00520    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames->str);
00521    if (resultset) {
00522       int uid;
00523       struct ast_datastore *odbc_store;
00524       uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00525       snprintf(buf, len, "%d", uid);
00526       odbc_store = ast_datastore_alloc(&odbc_info, buf);
00527       if (!odbc_store) {
00528          ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel.  Results fail.\n");
00529          odbc_datastore_free(resultset);
00530          SQLCloseCursor(stmt);
00531          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00532          ast_odbc_release_obj(obj);
00533          if (chan)
00534             ast_autoservice_stop(chan);
00535          if (bogus_chan)
00536             ast_channel_free(chan);
00537          ast_free(sql);
00538          return -1;
00539       }
00540       odbc_store->data = resultset;
00541       ast_channel_datastore_add(chan, odbc_store);
00542    }
00543    SQLCloseCursor(stmt);
00544    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00545    ast_odbc_release_obj(obj);
00546    if (chan)
00547       ast_autoservice_stop(chan);
00548    if (bogus_chan)
00549       ast_channel_free(chan);
00550    ast_free(sql);
00551    return 0;
00552 }

static int acf_odbc_write ( struct ast_channel chan,
const char *  cmd,
char *  s,
const char *  value 
) [static]

Definition at line 130 of file func_odbc.c.

References acf_odbc_query::acf, AST_APP_ARG, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_alloc, ast_channel_free(), AST_DECLARE_APP_ARGS, ast_free, ast_log(), ast_odbc_direct_execute(), ast_odbc_request_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_buffer, ast_str_create(), ast_str_make_space(), ast_str_size, ast_str_strlen, ast_str_update, ast_strdupa, ast_strlen_zero(), buf, chan, dsn, generic_execute(), acf_odbc_query::list, LOG_ERROR, ast_custom_function::name, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), pbx_substitute_variables_helper(), acf_odbc_query::sql_write, and acf_odbc_query::writehandle.

Referenced by init_acf_query().

00131 {
00132    struct odbc_obj *obj = NULL;
00133    struct acf_odbc_query *query;
00134    char *t, varname[15];
00135    int i, dsn, bogus_chan = 0;
00136    AST_DECLARE_APP_ARGS(values,
00137       AST_APP_ARG(field)[100];
00138    );
00139    AST_DECLARE_APP_ARGS(args,
00140       AST_APP_ARG(field)[100];
00141    );
00142    SQLHSTMT stmt = NULL;
00143    SQLLEN rows=0;
00144    struct ast_str *buf = ast_str_create(16);
00145 
00146    if (!buf) {
00147       return -1;
00148    }
00149 
00150    AST_RWLIST_RDLOCK(&queries);
00151    AST_RWLIST_TRAVERSE(&queries, query, list) {
00152       if (!strcmp(query->acf->name, cmd)) {
00153          break;
00154       }
00155    }
00156 
00157    if (!query) {
00158       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00159       AST_RWLIST_UNLOCK(&queries);
00160       ast_free(buf);
00161       return -1;
00162    }
00163 
00164    if (!chan) {
00165       if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00166          bogus_chan = 1;
00167    }
00168 
00169    if (chan)
00170       ast_autoservice_start(chan);
00171 
00172    /* Parse our arguments */
00173    t = value ? ast_strdupa(value) : "";
00174 
00175    if (!s || !t) {
00176       ast_log(LOG_ERROR, "Out of memory\n");
00177       AST_RWLIST_UNLOCK(&queries);
00178       if (chan)
00179          ast_autoservice_stop(chan);
00180       if (bogus_chan)
00181          ast_channel_free(chan);
00182       ast_free(buf);
00183       return -1;
00184    }
00185 
00186    AST_STANDARD_APP_ARGS(args, s);
00187    for (i = 0; i < args.argc; i++) {
00188       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00189       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00190    }
00191 
00192    /* Parse values, just like arguments */
00193    AST_STANDARD_APP_ARGS(values, t);
00194    for (i = 0; i < values.argc; i++) {
00195       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00196       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00197    }
00198 
00199    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
00200    pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00201 
00202    do {
00203       ast_str_make_space(&buf, 2 * (strlen(query->sql_write) > ast_str_size(buf) ? strlen(query->sql_write) : ast_str_size(buf)));
00204       pbx_substitute_variables_helper(chan, query->sql_write, ast_str_buffer(buf), ast_str_size(buf) - 1);
00205       ast_str_update(buf);
00206    } while (ast_str_strlen(buf) > ast_str_size(buf) - 5);
00207 
00208    /* Restore prior values */
00209    for (i = 0; i < args.argc; i++) {
00210       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00211       pbx_builtin_setvar_helper(chan, varname, NULL);
00212    }
00213 
00214    for (i = 0; i < values.argc; i++) {
00215       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00216       pbx_builtin_setvar_helper(chan, varname, NULL);
00217    }
00218    pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00219 
00220    AST_RWLIST_UNLOCK(&queries);
00221 
00222    for (dsn = 0; dsn < 5; dsn++) {
00223       if (!ast_strlen_zero(query->writehandle[dsn])) {
00224          obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00225          if (obj)
00226             stmt = ast_odbc_direct_execute(obj, generic_execute, buf->str);
00227       }
00228       if (stmt)
00229          break;
00230    }
00231 
00232    if (stmt) {
00233       /* Rows affected */
00234       SQLRowCount(stmt, &rows);
00235    }
00236 
00237    /* Output the affected rows, for all cases.  In the event of failure, we
00238     * flag this as -1 rows.  Note that this is different from 0 affected rows
00239     * which would be the case if we succeeded in our query, but the values did
00240     * not change. */
00241    snprintf(varname, sizeof(varname), "%d", (int)rows);
00242    pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00243 
00244    if (stmt) {
00245       SQLCloseCursor(stmt);
00246       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00247    }
00248    if (obj)
00249       ast_odbc_release_obj(obj);
00250 
00251    if (chan)
00252       ast_autoservice_stop(chan);
00253    if (bogus_chan)
00254       ast_channel_free(chan);
00255    ast_free(buf);
00256 
00257    return 0;
00258 }

static int exec_odbcfinish ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 626 of file func_odbc.c.

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

Referenced by load_module().

00627 {
00628    struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00629    if (!store) /* Already freed; no big deal. */
00630       return 0;
00631    ast_channel_datastore_remove(chan, store);
00632    ast_datastore_free(store);
00633    return 0;
00634 }

static int free_acf_query ( struct acf_odbc_query query  )  [static]

Definition at line 824 of file func_odbc.c.

References acf_odbc_query::acf, ast_free, ast_custom_function::desc, ast_custom_function::name, and ast_custom_function::syntax.

Referenced by reload(), and unload_module().

00825 {
00826    if (query) {
00827       if (query->acf) {
00828          if (query->acf->name)
00829             ast_free((char *)query->acf->name);
00830          if (query->acf->syntax)
00831             ast_free((char *)query->acf->syntax);
00832          if (query->acf->desc)
00833             ast_free((char *)query->acf->desc);
00834          ast_free(query->acf);
00835       }
00836       ast_free(query);
00837    }
00838    return 0;
00839 }

static SQLHSTMT generic_execute ( struct odbc_obj obj,
void *  data 
) [static]

Definition at line 104 of file func_odbc.c.

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

Referenced by acf_odbc_read(), and acf_odbc_write().

00105 {
00106    int res;
00107    char *sql = data;
00108    SQLHSTMT stmt;
00109 
00110    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00111    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00112       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00113       return NULL;
00114    }
00115 
00116    res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
00117    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00118       ast_log(LOG_WARNING, "SQL Exec Direct failed![%s]\n", sql);
00119       SQLCloseCursor(stmt);
00120       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00121       return NULL;
00122    }
00123 
00124    return stmt;
00125 }

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

Definition at line 636 of file func_odbc.c.

References acf_odbc_query::acf, 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_strlen_zero(), ast_variable_retrieve(), dsn, errno, LOG_WARNING, OPT_ESCAPECOMMAS, OPT_MULTIROW, ast_custom_function::read, and ast_custom_function::synopsis.

Referenced by load_module(), and reload().

00637 {
00638    const char *tmp;
00639    int i;
00640    int res;
00641 
00642    if (!cfg || !catg) {
00643       return EINVAL;
00644    }
00645 
00646    *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00647    if (! (*query))
00648       return ENOMEM;
00649 
00650    if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00651       char *tmp2 = ast_strdupa(tmp);
00652       AST_DECLARE_APP_ARGS(writeconf,
00653          AST_APP_ARG(dsn)[5];
00654       );
00655       AST_STANDARD_APP_ARGS(writeconf, tmp2);
00656       for (i = 0; i < 5; i++) {
00657          if (!ast_strlen_zero(writeconf.dsn[i]))
00658             ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00659       }
00660    }
00661 
00662    if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00663       char *tmp2 = ast_strdupa(tmp);
00664       AST_DECLARE_APP_ARGS(readconf,
00665          AST_APP_ARG(dsn)[5];
00666       );
00667       AST_STANDARD_APP_ARGS(readconf, tmp2);
00668       for (i = 0; i < 5; i++) {
00669          if (!ast_strlen_zero(readconf.dsn[i]))
00670             ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00671       }
00672    } else {
00673       /* If no separate readhandle, then use the writehandle for reading */
00674       for (i = 0; i < 5; i++) {
00675          if (!ast_strlen_zero((*query)->writehandle[i]))
00676             ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00677       }
00678    }
00679 
00680    if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00681       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00682    else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00683       ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
00684       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00685    }
00686 
00687    if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00688       ast_free(*query);
00689       *query = NULL;
00690       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00691       return EINVAL;
00692    }
00693 
00694    if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00695       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00696    else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00697       ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
00698       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00699    }
00700 
00701    if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00702       ast_free(*query);
00703       *query = NULL;
00704       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00705       return EINVAL;
00706    }
00707 
00708    /* Allow escaping of embedded commas in fields to be turned off */
00709    ast_set_flag((*query), OPT_ESCAPECOMMAS);
00710    if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00711       if (ast_false(tmp))
00712          ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00713    }
00714 
00715    if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00716       if (strcasecmp(tmp, "multirow") == 0)
00717          ast_set_flag((*query), OPT_MULTIROW);
00718       if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00719          sscanf(tmp, "%30d", &((*query)->rowlimit));
00720    }
00721 
00722    (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00723    if (! (*query)->acf) {
00724       ast_free(*query);
00725       *query = NULL;
00726       return ENOMEM;
00727    }
00728 
00729    if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00730       if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00731          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00732       }
00733    } else {
00734       if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00735          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00736       }
00737    }
00738 
00739    if (!((*query)->acf->name)) {
00740       ast_free((*query)->acf);
00741       ast_free(*query);
00742       *query = NULL;
00743       return ENOMEM;
00744    }
00745 
00746    if (asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name) < 0) {
00747       ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00748       (*query)->acf->syntax = NULL;
00749    }
00750 
00751    if (!((*query)->acf->syntax)) {
00752       ast_free((char *)(*query)->acf->name);
00753       ast_free((*query)->acf);
00754       ast_free(*query);
00755       *query = NULL;
00756       return ENOMEM;
00757    }
00758 
00759    (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00760 
00761    res = 0;
00762    if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00763       res = asprintf((char **)&((*query)->acf->desc),
00764                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00765                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00766                 "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
00767                 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00768                 "\nRead:\n%s\n\nWrite:\n%s\n",
00769                 (*query)->sql_read,
00770                 (*query)->sql_write);
00771    } else if (!ast_strlen_zero((*query)->sql_read)) {
00772       res = asprintf((char **)&((*query)->acf->desc),
00773                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00774                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00775                 "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
00776                 (*query)->sql_read);
00777    } else if (!ast_strlen_zero((*query)->sql_write)) {
00778       res = asprintf((char **)&((*query)->acf->desc),
00779                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00780                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00781                 "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
00782                 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00783                 "This function may only be set.\nSQL:\n%s\n",
00784                 (*query)->sql_write);
00785    } else {
00786       ast_free((char *)(*query)->acf->syntax);
00787       ast_free((char *)(*query)->acf->name);
00788       ast_free((*query)->acf);
00789       ast_free(*query);
00790       ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute.  Ignoring.\n", catg);
00791       return EINVAL;
00792    }
00793 
00794    if (res < 0) {
00795       ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00796       (*query)->acf->desc = NULL;
00797    }
00798 
00799 
00800    if (!((*query)->acf->desc)) {
00801       ast_free((char *)(*query)->acf->syntax);
00802       ast_free((char *)(*query)->acf->name);
00803       ast_free((*query)->acf);
00804       ast_free(*query);
00805       *query = NULL;
00806       return ENOMEM;
00807    }
00808 
00809    if (ast_strlen_zero((*query)->sql_read)) {
00810       (*query)->acf->read = NULL;
00811    } else {
00812       (*query)->acf->read = acf_odbc_read;
00813    }
00814 
00815    if (ast_strlen_zero((*query)->sql_write)) {
00816       (*query)->acf->write = NULL;
00817    } else {
00818       (*query)->acf->write = acf_odbc_write;
00819    }
00820 
00821    return 0;
00822 }

static int load_module ( void   )  [static]

Definition at line 841 of file func_odbc.c.

References ast_category_browse(), ast_config_destroy(), ast_config_load, ast_custom_function_register, ast_log(), AST_MODULE_LOAD_DECLINE, ast_register_application, AST_RWLIST_INSERT_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, config_flags, escape_function, exec_odbcfinish(), fetch_function, init_acf_query(), and LOG_NOTICE.

00842 {
00843    int res = 0;
00844    struct ast_config *cfg;
00845    char *catg;
00846    struct ast_flags config_flags = { 0 };
00847 
00848    res |= ast_custom_function_register(&fetch_function);
00849    res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
00850    AST_RWLIST_WRLOCK(&queries);
00851 
00852    cfg = ast_config_load(config, config_flags);
00853    if (!cfg) {
00854       ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
00855       AST_RWLIST_UNLOCK(&queries);
00856       return AST_MODULE_LOAD_DECLINE;
00857    }
00858 
00859    for (catg = ast_category_browse(cfg, NULL);
00860         catg;
00861         catg = ast_category_browse(cfg, catg)) {
00862       struct acf_odbc_query *query = NULL;
00863       int err;
00864 
00865       if ((err = init_acf_query(cfg, catg, &query))) {
00866          if (err == ENOMEM)
00867             ast_log(LOG_ERROR, "Out of memory\n");
00868          else if (err == EINVAL)
00869             ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
00870          else
00871             ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
00872       } else {
00873          AST_RWLIST_INSERT_HEAD(&queries, query, list);
00874          ast_custom_function_register(query->acf);
00875       }
00876    }
00877 
00878    ast_config_destroy(cfg);
00879    res |= ast_custom_function_register(&escape_function);
00880 
00881    AST_RWLIST_UNLOCK(&queries);
00882    return res;
00883 }

static void odbc_datastore_free ( void *  data  )  [static]

Definition at line 91 of file func_odbc.c.

References ast_free, AST_LIST_HEAD_DESTROY, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and acf_odbc_query::list.

00092 {
00093    struct odbc_datastore *result = data;
00094    struct odbc_datastore_row *row;
00095    AST_LIST_LOCK(result);
00096    while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
00097       ast_free(row);
00098    }
00099    AST_LIST_UNLOCK(result);
00100    AST_LIST_HEAD_DESTROY(result);
00101    ast_free(result);
00102 }

static int reload ( void   )  [static]

Definition at line 910 of file func_odbc.c.

References acf_odbc_query::acf, 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_flags, CONFIG_STATUS_FILEUNCHANGED, free_acf_query(), init_acf_query(), and LOG_WARNING.

00911 {
00912    int res = 0;
00913    struct ast_config *cfg;
00914    struct acf_odbc_query *oldquery;
00915    char *catg;
00916    struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
00917 
00918    cfg = ast_config_load(config, config_flags);
00919    if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00920       return 0;
00921 
00922    AST_RWLIST_WRLOCK(&queries);
00923 
00924    while (!AST_RWLIST_EMPTY(&queries)) {
00925       oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
00926       ast_custom_function_unregister(oldquery->acf);
00927       free_acf_query(oldquery);
00928    }
00929 
00930    if (!cfg) {
00931       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00932       goto reload_out;
00933    }
00934 
00935    for (catg = ast_category_browse(cfg, NULL);
00936         catg;
00937         catg = ast_category_browse(cfg, catg)) {
00938       struct acf_odbc_query *query = NULL;
00939 
00940       if (init_acf_query(cfg, catg, &query)) {
00941          ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00942       } else {
00943          AST_RWLIST_INSERT_HEAD(&queries, query, list);
00944          ast_custom_function_register(query->acf);
00945       }
00946    }
00947 
00948    ast_config_destroy(cfg);
00949 reload_out:
00950    AST_RWLIST_UNLOCK(&queries);
00951    return res;
00952 }

static int unload_module ( void   )  [static]

Definition at line 885 of file func_odbc.c.

References acf_odbc_query::acf, ast_custom_function_unregister(), AST_RWLIST_EMPTY, AST_RWLIST_REMOVE_HEAD, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, ast_unregister_application(), escape_function, fetch_function, free_acf_query(), and acf_odbc_query::list.

00886 {
00887    struct acf_odbc_query *query;
00888    int res = 0;
00889 
00890    AST_RWLIST_WRLOCK(&queries);
00891    while (!AST_RWLIST_EMPTY(&queries)) {
00892       query = AST_RWLIST_REMOVE_HEAD(&queries, list);
00893       ast_custom_function_unregister(query->acf);
00894       free_acf_query(query);
00895    }
00896 
00897    res |= ast_custom_function_unregister(&escape_function);
00898    res |= ast_custom_function_unregister(&fetch_function);
00899    res |= ast_unregister_application(app_odbcfinish);
00900 
00901    /* Allow any threads waiting for this lock to pass (avoids a race) */
00902    AST_RWLIST_UNLOCK(&queries);
00903    usleep(1);
00904    AST_RWLIST_WRLOCK(&queries);
00905 
00906    AST_RWLIST_UNLOCK(&queries);
00907    return 0;
00908 }


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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 960 of file func_odbc.c.

char* app_odbcfinish = "ODBCFinish" [static]

Definition at line 619 of file func_odbc.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 960 of file func_odbc.c.

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

Definition at line 88 of file func_odbc.c.

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

Definition at line 89 of file func_odbc.c.

Referenced by acf_odbc_read().

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

Definition at line 47 of file func_odbc.c.

char* desc_odbcfinish [static]

Initial value:

"ODBCFinish(<result-id>)\n"
"  Clears any remaining rows of the specified resultset\n"

Definition at line 621 of file func_odbc.c.

struct ast_custom_function escape_function [static]

Definition at line 570 of file func_odbc.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function fetch_function [static]

Definition at line 607 of file func_odbc.c.

Referenced by load_module(), and unload_module().

struct ast_datastore_info odbc_info

Initial value:

 {
   .type = "FUNC_ODBC",
   .destroy = odbc_datastore_free,
}

Definition at line 67 of file func_odbc.c.

Referenced by acf_fetch(), and exec_odbcfinish().

enum { ... } odbc_option_flags

int resultcount = 0 [static]

Definition at line 86 of file func_odbc.c.

char* syn_odbcfinish = "Clear the resultset of a successful multirow query" [static]

Definition at line 620 of file func_odbc.c.


Generated on Wed Aug 18 22:34:24 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7