Tue Mar 2 17:33:43 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"
Include dependency graph for func_odbc.c:

Go to the source code of this file.

Data Structures

struct  acf_odbc_query
struct  odbc_datastore
struct  odbc_datastore_row
struct  queries

Enumerations

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

Functions

static void __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
struct 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 955 of file func_odbc.c.

static void __unreg_module ( void   )  [static]

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

00550 {
00551    char *out = buf;
00552 
00553    for (; *data && out - buf < len; data++) {
00554       if (*data == '\'') {
00555          *out = '\'';
00556          out++;
00557       }
00558       *out++ = *data;
00559    }
00560    *out = '\0';
00561 
00562    return 0;
00563 }

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

Definition at line 577 of file func_odbc.c.

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

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

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

Definition at line 258 of file func_odbc.c.

References acf_odbc_query::acf, AST_APP_ARG, ast_atomic_fetchadd_int(), ast_autoservice_start(), ast_autoservice_stop(), ast_calloc, ast_channel_alloc, ast_channel_datastore_add(), ast_channel_free(), ast_copy_string(), ast_datastore_alloc, ast_debug, AST_DECLARE_APP_ARGS, ast_free, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, ast_log(), ast_odbc_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_create(), ast_str_make_space(), ast_str_reset(), ast_str_thread_get(), ast_strlen_zero(), ast_test_flag, ast_verb, coldata_buf, colnames_buf, ast_datastore::data, dsn, generic_execute(), ast_str::len, acf_odbc_query::list, LOG_ERROR, LOG_WARNING, ast_custom_function::name, odbc_datastore_free(), odbc_info, OPT_ESCAPECOMMAS, OPT_MULTIROW, pbx_builtin_pushvar_helper(), pbx_builtin_setvar_helper(), pbx_substitute_variables_helper(), acf_odbc_query::readhandle, resultcount, acf_odbc_query::rowlimit, acf_odbc_query::sql_read, ast_str::str, and ast_str::used.

Referenced by init_acf_query().

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

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_release_obj(), ast_odbc_request_obj(), AST_RWLIST_RDLOCK, AST_RWLIST_TRAVERSE, AST_RWLIST_UNLOCK, AST_STANDARD_APP_ARGS, ast_str_create(), ast_str_make_space(), ast_strdupa, ast_strlen_zero(), buf, dsn, generic_execute(), ast_str::len, 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, ast_str::str, 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    ast_str_make_space(&buf, strlen(query->sql_write) * 2);
00173 
00174    /* Parse our arguments */
00175    t = value ? ast_strdupa(value) : "";
00176 
00177    if (!s || !t) {
00178       ast_log(LOG_ERROR, "Out of memory\n");
00179       AST_RWLIST_UNLOCK(&queries);
00180       if (chan)
00181          ast_autoservice_stop(chan);
00182       if (bogus_chan)
00183          ast_channel_free(chan);
00184       ast_free(buf);
00185       return -1;
00186    }
00187 
00188    AST_STANDARD_APP_ARGS(args, s);
00189    for (i = 0; i < args.argc; i++) {
00190       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00191       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00192    }
00193 
00194    /* Parse values, just like arguments */
00195    AST_STANDARD_APP_ARGS(values, t);
00196    for (i = 0; i < values.argc; i++) {
00197       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00198       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00199    }
00200 
00201    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
00202    pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00203 
00204    pbx_substitute_variables_helper(chan, query->sql_write, buf->str, buf->len - 1);
00205 
00206    /* Restore prior values */
00207    for (i = 0; i < args.argc; i++) {
00208       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00209       pbx_builtin_setvar_helper(chan, varname, NULL);
00210    }
00211 
00212    for (i = 0; i < values.argc; i++) {
00213       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00214       pbx_builtin_setvar_helper(chan, varname, NULL);
00215    }
00216    pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00217 
00218    AST_RWLIST_UNLOCK(&queries);
00219 
00220    for (dsn = 0; dsn < 5; dsn++) {
00221       if (!ast_strlen_zero(query->writehandle[dsn])) {
00222          obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00223          if (obj)
00224             stmt = ast_odbc_direct_execute(obj, generic_execute, buf->str);
00225       }
00226       if (stmt)
00227          break;
00228    }
00229 
00230    if (stmt) {
00231       /* Rows affected */
00232       SQLRowCount(stmt, &rows);
00233    }
00234 
00235    /* Output the affected rows, for all cases.  In the event of failure, we
00236     * flag this as -1 rows.  Note that this is different from 0 affected rows
00237     * which would be the case if we succeeded in our query, but the values did
00238     * not change. */
00239    snprintf(varname, sizeof(varname), "%d", (int)rows);
00240    pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00241 
00242    if (stmt) {
00243       SQLCloseCursor(stmt);
00244       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00245    }
00246    if (obj)
00247       ast_odbc_release_obj(obj);
00248 
00249    if (chan)
00250       ast_autoservice_stop(chan);
00251    if (bogus_chan)
00252       ast_channel_free(chan);
00253    ast_free(buf);
00254 
00255    return 0;
00256 }

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

Definition at line 621 of file func_odbc.c.

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

Referenced by load_module().

00622 {
00623    struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00624    if (!store) /* Already freed; no big deal. */
00625       return 0;
00626    ast_channel_datastore_remove(chan, store);
00627    ast_datastore_free(store);
00628    return 0;
00629 }

static int free_acf_query ( struct acf_odbc_query query  )  [static]

Definition at line 819 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().

00820 {
00821    if (query) {
00822       if (query->acf) {
00823          if (query->acf->name)
00824             ast_free((char *)query->acf->name);
00825          if (query->acf->syntax)
00826             ast_free((char *)query->acf->syntax);
00827          if (query->acf->desc)
00828             ast_free((char *)query->acf->desc);
00829          ast_free(query->acf);
00830       }
00831       ast_free(query);
00832    }
00833    return 0;
00834 }

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 631 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_ERROR, LOG_WARNING, OPT_ESCAPECOMMAS, OPT_MULTIROW, ast_custom_function::read, and ast_custom_function::synopsis.

Referenced by load_module(), and reload().

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

static int load_module ( void   )  [static]

Definition at line 836 of file func_odbc.c.

References acf_odbc_query::acf, app_odbcfinish, 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, desc_odbcfinish, escape_function, exec_odbcfinish(), fetch_function, init_acf_query(), acf_odbc_query::list, LOG_ERROR, LOG_NOTICE, and syn_odbcfinish.

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

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.

Referenced by acf_odbc_read().

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 905 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_STATUS_FILEUNCHANGED, free_acf_query(), init_acf_query(), acf_odbc_query::list, LOG_ERROR, and LOG_WARNING.

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

static int unload_module ( void   )  [static]

Definition at line 880 of file func_odbc.c.

References acf_odbc_query::acf, app_odbcfinish, 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.

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


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

char* app_odbcfinish = "ODBCFinish" [static]

Definition at line 614 of file func_odbc.c.

Referenced by load_module(), and unload_module().

Definition at line 955 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.

Referenced by acf_odbc_read().

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

Referenced by load_module().

Definition at line 565 of file func_odbc.c.

Referenced by load_module(), and unload_module().

Definition at line 602 of file func_odbc.c.

Referenced by load_module(), and unload_module().

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

Definition at line 67 of file func_odbc.c.

Referenced by acf_fetch(), acf_odbc_read(), and exec_odbcfinish().

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

Definition at line 86 of file func_odbc.c.

Referenced by acf_odbc_read().

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

Definition at line 615 of file func_odbc.c.

Referenced by load_module().


Generated on 2 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1