Mon Sep 20 2010 00:22:52

Asterisk developer's documentation


func_odbc.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (c) 2005, 2006 Tilghman Lesher
00005  * Copyright (c) 2008 Digium, Inc.
00006  *
00007  * Tilghman Lesher <func_odbc__200508@the-tilghman.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*!
00021  * \file
00022  *
00023  * \brief ODBC lookups
00024  *
00025  * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
00026  *
00027  * \ingroup functions
00028  */
00029 
00030 /*** MODULEINFO
00031    <depend>res_odbc</depend>
00032  ***/
00033 
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211580 $")
00037 
00038 #include "asterisk/module.h"
00039 #include "asterisk/file.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/res_odbc.h"
00044 #include "asterisk/app.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/strings.h"
00047 
00048 /*** DOCUMENTATION
00049    <function name="ODBC_FETCH" language="en_US">
00050       <synopsis>
00051          Fetch a row from a multirow query.
00052       </synopsis>
00053       <syntax>
00054          <parameter name="result-id" required="true" />
00055       </syntax>
00056       <description>
00057          <para>For queries which are marked as mode=multirow, the original 
00058          query returns a <replaceable>result-id</replaceable> from which results 
00059          may be fetched.  This function implements the actual fetch of the results.</para>
00060          <para>This also sets <variable>ODBC_FETCH_STATUS</variable>.</para>
00061          <variablelist>
00062             <variable name="ODBC_FETCH_STATUS">
00063                <value name="SUCESS">
00064                   If rows are available.
00065                </value>
00066                <value name="FAILURE">
00067                   If no rows are available.
00068                </value>
00069             </variable>
00070          </variablelist>
00071       </description>
00072    </function>
00073    <application name="ODBCFinish" language="en_US">
00074       <synopsis>
00075          Clear the resultset of a sucessful multirow query.
00076       </synopsis>
00077       <syntax>
00078          <parameter name="result-id" required="true" />
00079       </syntax>
00080       <description>
00081          <para>For queries which are marked as mode=multirow, this will clear 
00082          any remaining rows of the specified resultset.</para>
00083       </description>
00084    </application>
00085    <function name="SQL_ESC" language="en_US">
00086       <synopsis>
00087          Escapes single ticks for use in SQL statements.
00088       </synopsis>
00089       <syntax>
00090          <parameter name="string" required="true" />
00091       </syntax>
00092       <description>
00093          <para>Used in SQL templates to escape data which may contain single ticks 
00094          <literal>'</literal> which are otherwise used to delimit data.</para>
00095          <para>Example: SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'</para>
00096       </description>
00097    </function>
00098  ***/
00099 
00100 static char *config = "func_odbc.conf";
00101 
00102 enum {
00103    OPT_ESCAPECOMMAS =   (1 << 0),
00104    OPT_MULTIROW     =   (1 << 1),
00105 } odbc_option_flags;
00106 
00107 struct acf_odbc_query {
00108    AST_RWLIST_ENTRY(acf_odbc_query) list;
00109    char readhandle[5][30];
00110    char writehandle[5][30];
00111    char sql_read[2048];
00112    char sql_write[2048];
00113    char sql_insert[2048];
00114    unsigned int flags;
00115    int rowlimit;
00116    struct ast_custom_function *acf;
00117 };
00118 
00119 static void odbc_datastore_free(void *data);
00120 
00121 struct ast_datastore_info odbc_info = {
00122    .type = "FUNC_ODBC",
00123    .destroy = odbc_datastore_free,
00124 };
00125 
00126 /* For storing each result row */
00127 struct odbc_datastore_row {
00128    AST_LIST_ENTRY(odbc_datastore_row) list;
00129    char data[0];
00130 };
00131 
00132 /* For storing each result set */
00133 struct odbc_datastore {
00134    AST_LIST_HEAD(, odbc_datastore_row);
00135    char names[0];
00136 };
00137 
00138 AST_RWLIST_HEAD_STATIC(queries, acf_odbc_query);
00139 
00140 static int resultcount = 0;
00141 
00142 AST_THREADSTORAGE(sql_buf);
00143 AST_THREADSTORAGE(sql2_buf);
00144 AST_THREADSTORAGE(coldata_buf);
00145 AST_THREADSTORAGE(colnames_buf);
00146 
00147 static void odbc_datastore_free(void *data)
00148 {
00149    struct odbc_datastore *result = data;
00150    struct odbc_datastore_row *row;
00151    AST_LIST_LOCK(result);
00152    while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
00153       ast_free(row);
00154    }
00155    AST_LIST_UNLOCK(result);
00156    AST_LIST_HEAD_DESTROY(result);
00157    ast_free(result);
00158 }
00159 
00160 static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
00161 {
00162    int res;
00163    char *sql = data;
00164    SQLHSTMT stmt;
00165 
00166    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00167    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00168       ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
00169       return NULL;
00170    }
00171 
00172    res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
00173    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00174       if (res == SQL_ERROR) {
00175          int i;
00176          SQLINTEGER nativeerror=0, numfields=0;
00177          SQLSMALLINT diagbytes=0;
00178          unsigned char state[10], diagnostic[256];
00179 
00180          SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00181          for (i = 0; i < numfields; i++) {
00182             SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00183             ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00184             if (i > 10) {
00185                ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00186                break;
00187             }
00188          }
00189       }
00190 
00191       ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
00192       SQLCloseCursor(stmt);
00193       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00194       return NULL;
00195    }
00196 
00197    return stmt;
00198 }
00199 
00200 /*
00201  * Master control routine
00202  */
00203 static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
00204 {
00205    struct odbc_obj *obj = NULL;
00206    struct acf_odbc_query *query;
00207    char *t, varname[15];
00208    int i, dsn, bogus_chan = 0;
00209    int transactional = 0;
00210    AST_DECLARE_APP_ARGS(values,
00211       AST_APP_ARG(field)[100];
00212    );
00213    AST_DECLARE_APP_ARGS(args,
00214       AST_APP_ARG(field)[100];
00215    );
00216    SQLHSTMT stmt = NULL;
00217    SQLLEN rows=0;
00218    struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
00219    struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
00220    const char *status = "FAILURE";
00221 
00222    if (!buf) {
00223       return -1;
00224    }
00225 
00226    AST_RWLIST_RDLOCK(&queries);
00227    AST_RWLIST_TRAVERSE(&queries, query, list) {
00228       if (!strcmp(query->acf->name, cmd)) {
00229          break;
00230       }
00231    }
00232 
00233    if (!query) {
00234       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00235       AST_RWLIST_UNLOCK(&queries);
00236       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00237       return -1;
00238    }
00239 
00240    if (!chan) {
00241       if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00242          bogus_chan = 1;
00243    }
00244 
00245    if (chan)
00246       ast_autoservice_start(chan);
00247 
00248    ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
00249    ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
00250 
00251    /* Parse our arguments */
00252    t = value ? ast_strdupa(value) : "";
00253 
00254    if (!s || !t) {
00255       ast_log(LOG_ERROR, "Out of memory\n");
00256       AST_RWLIST_UNLOCK(&queries);
00257       if (chan)
00258          ast_autoservice_stop(chan);
00259       if (bogus_chan) {
00260          ast_channel_free(chan);
00261       } else {
00262          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00263       }
00264       return -1;
00265    }
00266 
00267    AST_STANDARD_APP_ARGS(args, s);
00268    for (i = 0; i < args.argc; i++) {
00269       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00270       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00271    }
00272 
00273    /* Parse values, just like arguments */
00274    AST_STANDARD_APP_ARGS(values, t);
00275    for (i = 0; i < values.argc; i++) {
00276       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00277       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00278    }
00279 
00280    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
00281    pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00282 
00283    ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
00284    ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);
00285 
00286    /* Restore prior values */
00287    for (i = 0; i < args.argc; i++) {
00288       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00289       pbx_builtin_setvar_helper(chan, varname, NULL);
00290    }
00291 
00292    for (i = 0; i < values.argc; i++) {
00293       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00294       pbx_builtin_setvar_helper(chan, varname, NULL);
00295    }
00296    pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00297 
00298    /*!\note
00299     * Okay, this part is confusing.  Transactions belong to a single database
00300     * handle.  Therefore, when working with transactions, we CANNOT failover
00301     * to multiple DSNs.  We MUST have a single handle all the way through the
00302     * transaction, or else we CANNOT enforce atomicity.
00303     */
00304    for (dsn = 0; dsn < 5; dsn++) {
00305       if (transactional) {
00306          /* This can only happen second time through or greater. */
00307          ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
00308       }
00309 
00310       if (!ast_strlen_zero(query->writehandle[dsn])) {
00311          if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
00312             transactional = 1;
00313          } else {
00314             obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00315             transactional = 0;
00316          }
00317          if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
00318             break;
00319          }
00320       }
00321 
00322       if (obj && !transactional) {
00323          ast_odbc_release_obj(obj);
00324       }
00325    }
00326 
00327    if (stmt && rows == 0 && ast_str_strlen(insertbuf) != 0) {
00328       SQLCloseCursor(stmt);
00329       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00330       for (dsn = 0; dsn < 5; dsn++) {
00331          if (!ast_strlen_zero(query->writehandle[dsn])) {
00332             obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00333             if (obj) {
00334                stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
00335             }
00336          }
00337          if (stmt) {
00338             status = "FAILOVER";
00339             SQLRowCount(stmt, &rows);
00340             break;
00341          }
00342       }
00343    } else if (stmt) {
00344       status = "SUCCESS";
00345       SQLRowCount(stmt, &rows);
00346    }
00347 
00348    AST_RWLIST_UNLOCK(&queries);
00349 
00350    /* Output the affected rows, for all cases.  In the event of failure, we
00351     * flag this as -1 rows.  Note that this is different from 0 affected rows
00352     * which would be the case if we succeeded in our query, but the values did
00353     * not change. */
00354    snprintf(varname, sizeof(varname), "%d", (int)rows);
00355    pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00356    pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00357 
00358    if (stmt) {
00359       SQLCloseCursor(stmt);
00360       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00361    }
00362    if (obj && !transactional) {
00363       ast_odbc_release_obj(obj);
00364       obj = NULL;
00365    }
00366 
00367    if (chan)
00368       ast_autoservice_stop(chan);
00369    if (bogus_chan)
00370       ast_channel_free(chan);
00371 
00372    return 0;
00373 }
00374 
00375 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
00376 {
00377    struct odbc_obj *obj = NULL;
00378    struct acf_odbc_query *query;
00379    char varname[15], rowcount[12] = "-1";
00380    struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
00381    int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
00382    AST_DECLARE_APP_ARGS(args,
00383       AST_APP_ARG(field)[100];
00384    );
00385    SQLHSTMT stmt = NULL;
00386    SQLSMALLINT colcount=0;
00387    SQLLEN indicator;
00388    SQLSMALLINT collength;
00389    struct odbc_datastore *resultset = NULL;
00390    struct odbc_datastore_row *row = NULL;
00391    struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
00392    const char *status = "FAILURE";
00393 
00394    if (!sql) {
00395       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00396       return -1;
00397    }
00398 
00399    ast_str_reset(colnames);
00400 
00401    AST_RWLIST_RDLOCK(&queries);
00402    AST_RWLIST_TRAVERSE(&queries, query, list) {
00403       if (!strcmp(query->acf->name, cmd)) {
00404          break;
00405       }
00406    }
00407 
00408    if (!query) {
00409       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00410       AST_RWLIST_UNLOCK(&queries);
00411       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00412       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00413       return -1;
00414    }
00415 
00416    if (!chan) {
00417       if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc"))) {
00418          bogus_chan = 1;
00419       }
00420    }
00421 
00422    if (chan) {
00423       ast_autoservice_start(chan);
00424    }
00425 
00426    AST_STANDARD_APP_ARGS(args, s);
00427    for (x = 0; x < args.argc; x++) {
00428       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00429       pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00430    }
00431 
00432    ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
00433 
00434    /* Restore prior values */
00435    for (x = 0; x < args.argc; x++) {
00436       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00437       pbx_builtin_setvar_helper(chan, varname, NULL);
00438    }
00439 
00440    /* Save these flags, so we can release the lock */
00441    escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00442    if (ast_test_flag(query, OPT_MULTIROW)) {
00443       resultset = ast_calloc(1, sizeof(*resultset));
00444       AST_LIST_HEAD_INIT(resultset);
00445       if (query->rowlimit) {
00446          rowlimit = query->rowlimit;
00447       } else {
00448          rowlimit = INT_MAX;
00449       }
00450    }
00451    AST_RWLIST_UNLOCK(&queries);
00452 
00453    for (dsn = 0; dsn < 5; dsn++) {
00454       if (!ast_strlen_zero(query->readhandle[dsn])) {
00455          obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
00456          if (obj) {
00457             stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql));
00458          }
00459       }
00460       if (stmt) {
00461          break;
00462       }
00463    }
00464 
00465    if (!stmt) {
00466       ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
00467       if (obj) {
00468          ast_odbc_release_obj(obj);
00469          obj = NULL;
00470       }
00471       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00472       if (chan) {
00473          ast_autoservice_stop(chan);
00474       }
00475       if (bogus_chan) {
00476          ast_channel_free(chan);
00477       }
00478       return -1;
00479    }
00480 
00481    res = SQLNumResultCols(stmt, &colcount);
00482    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00483       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
00484       SQLCloseCursor(stmt);
00485       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00486       ast_odbc_release_obj(obj);
00487       obj = NULL;
00488       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00489       if (chan) {
00490          ast_autoservice_stop(chan);
00491       }
00492       if (bogus_chan) {
00493          ast_channel_free(chan);
00494       }
00495       return -1;
00496    }
00497 
00498    res = SQLFetch(stmt);
00499    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00500       int res1 = -1;
00501       if (res == SQL_NO_DATA) {
00502          ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
00503          res1 = 0;
00504          buf[0] = '\0';
00505          ast_copy_string(rowcount, "0", sizeof(rowcount));
00506          status = "NODATA";
00507       } else {
00508          ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
00509          status = "FETCHERROR";
00510       }
00511       SQLCloseCursor(stmt);
00512       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00513       ast_odbc_release_obj(obj);
00514       obj = NULL;
00515       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00516       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00517       if (chan)
00518          ast_autoservice_stop(chan);
00519       if (bogus_chan)
00520          ast_channel_free(chan);
00521       return res1;
00522    }
00523 
00524    status = "SUCCESS";
00525 
00526    for (y = 0; y < rowlimit; y++) {
00527       buf[0] = '\0';
00528       for (x = 0; x < colcount; x++) {
00529          int i;
00530          struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00531          char *ptrcoldata;
00532 
00533          if (y == 0) {
00534             char colname[256];
00535             SQLULEN maxcol;
00536 
00537             res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00538             ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00539             if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00540                snprintf(colname, sizeof(colname), "field%d", x);
00541             }
00542 
00543             ast_str_make_space(&coldata, maxcol + 1);
00544 
00545             if (ast_str_strlen(colnames)) {
00546                ast_str_append(&colnames, 0, ",");
00547             }
00548             ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
00549 
00550             if (resultset) {
00551                void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
00552                if (!tmp) {
00553                   ast_log(LOG_ERROR, "No space for a new resultset?\n");
00554                   ast_free(resultset);
00555                   SQLCloseCursor(stmt);
00556                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00557                   ast_odbc_release_obj(obj);
00558                   obj = NULL;
00559                   pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00560                   pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00561                   if (chan)
00562                      ast_autoservice_stop(chan);
00563                   if (bogus_chan)
00564                      ast_channel_free(chan);
00565                   return -1;
00566                }
00567                resultset = tmp;
00568                strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
00569             }
00570          }
00571 
00572          buflen = strlen(buf);
00573          res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
00574          if (indicator == SQL_NULL_DATA) {
00575             ast_debug(3, "Got NULL data\n");
00576             ast_str_reset(coldata);
00577             res = SQL_SUCCESS;
00578          }
00579 
00580          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00581             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
00582             y = -1;
00583             buf[0] = '\0';
00584             goto end_acf_read;
00585          }
00586 
00587          ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
00588 
00589          if (x) {
00590             buf[buflen++] = ',';
00591          }
00592 
00593          /* Copy data, encoding '\' and ',' for the argument parser */
00594          ptrcoldata = ast_str_buffer(coldata);
00595          for (i = 0; i < ast_str_strlen(coldata); i++) {
00596             if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
00597                buf[buflen++] = '\\';
00598             }
00599             buf[buflen++] = ptrcoldata[i];
00600 
00601             if (buflen >= len - 2) {
00602                break;
00603             }
00604 
00605             if (ptrcoldata[i] == '\0') {
00606                break;
00607             }
00608          }
00609 
00610          buf[buflen] = '\0';
00611          ast_debug(2, "buf is now set to '%s'\n", buf);
00612       }
00613       ast_debug(2, "buf is now set to '%s'\n", buf);
00614 
00615       if (resultset) {
00616          row = ast_calloc(1, sizeof(*row) + buflen + 1);
00617          if (!row) {
00618             ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00619             status = "MEMERROR";
00620             goto end_acf_read;
00621          }
00622          strcpy((char *)row + sizeof(*row), buf);
00623          AST_LIST_INSERT_TAIL(resultset, row, list);
00624 
00625          /* Get next row */
00626          res = SQLFetch(stmt);
00627          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00628             if (res != SQL_NO_DATA) {
00629                ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
00630             }
00631             /* Number of rows in the resultset */
00632             y++;
00633             break;
00634          }
00635       }
00636    }
00637 
00638 end_acf_read:
00639    snprintf(rowcount, sizeof(rowcount), "%d", y);
00640    pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00641    pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00642    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
00643    if (resultset) {
00644       int uid;
00645       struct ast_datastore *odbc_store;
00646       uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00647       snprintf(buf, len, "%d", uid);
00648       odbc_store = ast_datastore_alloc(&odbc_info, buf);
00649       if (!odbc_store) {
00650          ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel.  Results fail.\n");
00651          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00652          odbc_datastore_free(resultset);
00653          SQLCloseCursor(stmt);
00654          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00655          ast_odbc_release_obj(obj);
00656          obj = NULL;
00657          if (chan)
00658             ast_autoservice_stop(chan);
00659          if (bogus_chan)
00660             ast_channel_free(chan);
00661          return -1;
00662       }
00663       odbc_store->data = resultset;
00664       ast_channel_datastore_add(chan, odbc_store);
00665    }
00666    SQLCloseCursor(stmt);
00667    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00668    ast_odbc_release_obj(obj);
00669    obj = NULL;
00670    if (chan)
00671       ast_autoservice_stop(chan);
00672    if (bogus_chan)
00673       ast_channel_free(chan);
00674    return 0;
00675 }
00676 
00677 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00678 {
00679    char *out = buf;
00680 
00681    for (; *data && out - buf < len; data++) {
00682       if (*data == '\'') {
00683          *out = '\'';
00684          out++;
00685       }
00686       *out++ = *data;
00687    }
00688    *out = '\0';
00689 
00690    return 0;
00691 }
00692 
00693 static struct ast_custom_function escape_function = {
00694    .name = "SQL_ESC",
00695    .read = acf_escape,
00696    .write = NULL,
00697 };
00698 
00699 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00700 {
00701    struct ast_datastore *store;
00702    struct odbc_datastore *resultset;
00703    struct odbc_datastore_row *row;
00704    store = ast_channel_datastore_find(chan, &odbc_info, data);
00705    if (!store) {
00706       pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00707       return -1;
00708    }
00709    resultset = store->data;
00710    AST_LIST_LOCK(resultset);
00711    row = AST_LIST_REMOVE_HEAD(resultset, list);
00712    AST_LIST_UNLOCK(resultset);
00713    if (!row) {
00714       /* Cleanup datastore */
00715       ast_channel_datastore_remove(chan, store);
00716       ast_datastore_free(store);
00717       pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00718       return -1;
00719    }
00720    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00721    ast_copy_string(buf, row->data, len);
00722    ast_free(row);
00723    pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
00724    return 0;
00725 }
00726 
00727 static struct ast_custom_function fetch_function = {
00728    .name = "ODBC_FETCH",
00729    .read = acf_fetch,
00730    .write = NULL,
00731 };
00732 
00733 static char *app_odbcfinish = "ODBCFinish";
00734 
00735 static int exec_odbcfinish(struct ast_channel *chan, void *data)
00736 {
00737    struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00738    if (!store) /* Already freed; no big deal. */
00739       return 0;
00740    ast_channel_datastore_remove(chan, store);
00741    ast_datastore_free(store);
00742    return 0;
00743 }
00744 
00745 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00746 {
00747    const char *tmp;
00748    int i;
00749 
00750    if (!cfg || !catg) {
00751       return EINVAL;
00752    }
00753 
00754    *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00755    if (! (*query))
00756       return ENOMEM;
00757 
00758    if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00759       char *tmp2 = ast_strdupa(tmp);
00760       AST_DECLARE_APP_ARGS(writeconf,
00761          AST_APP_ARG(dsn)[5];
00762       );
00763       AST_STANDARD_APP_ARGS(writeconf, tmp2);
00764       for (i = 0; i < 5; i++) {
00765          if (!ast_strlen_zero(writeconf.dsn[i]))
00766             ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00767       }
00768    }
00769 
00770    if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00771       char *tmp2 = ast_strdupa(tmp);
00772       AST_DECLARE_APP_ARGS(readconf,
00773          AST_APP_ARG(dsn)[5];
00774       );
00775       AST_STANDARD_APP_ARGS(readconf, tmp2);
00776       for (i = 0; i < 5; i++) {
00777          if (!ast_strlen_zero(readconf.dsn[i]))
00778             ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00779       }
00780    } else {
00781       /* If no separate readhandle, then use the writehandle for reading */
00782       for (i = 0; i < 5; i++) {
00783          if (!ast_strlen_zero((*query)->writehandle[i]))
00784             ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00785       }
00786    }
00787 
00788    if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00789       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00790    else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00791       ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
00792       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00793    }
00794 
00795    if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00796       ast_free(*query);
00797       *query = NULL;
00798       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00799       return EINVAL;
00800    }
00801 
00802    if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00803       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00804    else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00805       ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
00806       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00807    }
00808 
00809    if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00810       ast_free(*query);
00811       *query = NULL;
00812       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00813       return EINVAL;
00814    }
00815 
00816    if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
00817       ast_copy_string((*query)->sql_insert, tmp, sizeof((*query)->sql_insert));
00818    }
00819 
00820    /* Allow escaping of embedded commas in fields to be turned off */
00821    ast_set_flag((*query), OPT_ESCAPECOMMAS);
00822    if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00823       if (ast_false(tmp))
00824          ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00825    }
00826 
00827    if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00828       if (strcasecmp(tmp, "multirow") == 0)
00829          ast_set_flag((*query), OPT_MULTIROW);
00830       if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00831          sscanf(tmp, "%30d", &((*query)->rowlimit));
00832    }
00833 
00834    (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00835    if (! (*query)->acf) {
00836       ast_free(*query);
00837       *query = NULL;
00838       return ENOMEM;
00839    }
00840    if (ast_string_field_init((*query)->acf, 128)) {
00841       ast_free((*query)->acf);
00842       ast_free(*query);
00843       *query = NULL;
00844       return ENOMEM;
00845    }
00846 
00847    if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00848       if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00849          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00850       }
00851    } else {
00852       if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00853          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00854       }
00855    }
00856 
00857    if (!((*query)->acf->name)) {
00858       ast_string_field_free_memory((*query)->acf);
00859       ast_free((*query)->acf);
00860       ast_free(*query);
00861       *query = NULL;
00862       return ENOMEM;
00863    }
00864 
00865    if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
00866       ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
00867    } else {
00868       ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
00869    }
00870 
00871    if (ast_strlen_zero((*query)->acf->syntax)) {
00872       ast_free((char *)(*query)->acf->name);
00873       ast_string_field_free_memory((*query)->acf);
00874       ast_free((*query)->acf);
00875       ast_free(*query);
00876       *query = NULL;
00877       return ENOMEM;
00878    }
00879 
00880    if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
00881       ast_string_field_set((*query)->acf, synopsis, tmp);
00882    } else {
00883       ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
00884    }
00885 
00886    if (ast_strlen_zero((*query)->acf->synopsis)) {
00887       ast_free((char *)(*query)->acf->name);
00888       ast_string_field_free_memory((*query)->acf);
00889       ast_free((*query)->acf);
00890       ast_free(*query);
00891       *query = NULL;
00892       return ENOMEM;
00893    }
00894 
00895    if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00896       ast_string_field_build((*query)->acf, desc,
00897                "Runs the following query, as defined in func_odbc.conf, performing\n"
00898                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00899                "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
00900                "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00901                "%s"
00902                "\nRead:\n%s\n\nWrite:\n%s\n%s%s%s",
00903                ast_strlen_zero((*query)->sql_insert) ? "" :
00904                   "If the write query affects no rows, the insert query will be\n"
00905                   "performed.\n",
00906                (*query)->sql_read,
00907                (*query)->sql_write,
00908                ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
00909                ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
00910                ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
00911    } else if (!ast_strlen_zero((*query)->sql_read)) {
00912       ast_string_field_build((*query)->acf, desc,
00913                   "Runs the following query, as defined in func_odbc.conf, performing\n"
00914                      "substitution of the arguments into the query as specified by ${ARG1},\n"
00915                   "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
00916                   (*query)->sql_read);
00917    } else if (!ast_strlen_zero((*query)->sql_write)) {
00918       ast_string_field_build((*query)->acf, desc,  
00919                "Runs the following query, as defined in func_odbc.conf, performing\n"
00920                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00921                "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
00922                "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00923                "This function may only be set.\n%sSQL:\n%s\n%s%s%s",
00924                ast_strlen_zero((*query)->sql_insert) ? "" :
00925                   "If the write query affects no rows, the insert query will be\n"
00926                   "performed.\n",
00927                (*query)->sql_write,
00928                ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
00929                ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
00930                ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
00931    } else {
00932       ast_string_field_free_memory((*query)->acf);
00933       ast_free((char *)(*query)->acf->name);
00934       ast_free((*query)->acf);
00935       ast_free(*query);
00936       ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute.  Ignoring.\n", catg);
00937       return EINVAL;
00938    }
00939 
00940    if (ast_strlen_zero((*query)->acf->desc)) {
00941       ast_string_field_free_memory((*query)->acf);
00942       ast_free((char *)(*query)->acf->name);
00943       ast_free((*query)->acf);
00944       ast_free(*query);
00945       *query = NULL;
00946       return ENOMEM;
00947    }
00948 
00949    if (ast_strlen_zero((*query)->sql_read)) {
00950       (*query)->acf->read = NULL;
00951    } else {
00952       (*query)->acf->read = acf_odbc_read;
00953    }
00954 
00955    if (ast_strlen_zero((*query)->sql_write)) {
00956       (*query)->acf->write = NULL;
00957    } else {
00958       (*query)->acf->write = acf_odbc_write;
00959    }
00960 
00961    return 0;
00962 }
00963 
00964 static int free_acf_query(struct acf_odbc_query *query)
00965 {
00966    if (query) {
00967       if (query->acf) {
00968          if (query->acf->name)
00969             ast_free((char *)query->acf->name);
00970          ast_string_field_free_memory(query->acf);
00971          ast_free(query->acf);
00972       }
00973       ast_free(query);
00974    }
00975    return 0;
00976 }
00977 
00978 static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00979 {
00980    AST_DECLARE_APP_ARGS(args,
00981       AST_APP_ARG(field)[100];
00982    );
00983    struct ast_str *sql;
00984    char *char_args, varname[10];
00985    struct acf_odbc_query *query;
00986    struct ast_channel *chan;
00987    int i;
00988 
00989    switch (cmd) {
00990    case CLI_INIT:
00991       e->command = "odbc read";
00992       e->usage =
00993          "Usage: odbc read <name> <args> [exec]\n"
00994          "       Evaluates the SQL provided in the ODBC function <name>, and\n"
00995          "       optionally executes the function.  This function is intended for\n"
00996          "       testing purposes.  Remember to quote arguments containing spaces.\n";
00997       return NULL;
00998    case CLI_GENERATE:
00999       if (a->pos == 2) {
01000          int wordlen = strlen(a->word), which = 0;
01001          /* Complete function name */
01002          AST_RWLIST_RDLOCK(&queries);
01003          AST_RWLIST_TRAVERSE(&queries, query, list) {
01004             if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01005                if (++which > a->n) {
01006                   char *res = ast_strdup(query->acf->name);
01007                   AST_RWLIST_UNLOCK(&queries);
01008                   return res;
01009                }
01010             }
01011          }
01012          AST_RWLIST_UNLOCK(&queries);
01013          return NULL;
01014       } else if (a->pos == 4) {
01015          return a->n == 0 ? ast_strdup("exec") : NULL;
01016       } else {
01017          return NULL;
01018       }
01019    }
01020 
01021    if (a->argc < 4 || a->argc > 5) {
01022       return CLI_SHOWUSAGE;
01023    }
01024 
01025    sql = ast_str_thread_get(&sql_buf, 16);
01026    if (!sql) {
01027       return CLI_FAILURE;
01028    }
01029 
01030    AST_RWLIST_RDLOCK(&queries);
01031    AST_RWLIST_TRAVERSE(&queries, query, list) {
01032       if (!strcmp(query->acf->name, a->argv[2])) {
01033          break;
01034       }
01035    }
01036 
01037    if (!query) {
01038       ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01039       AST_RWLIST_UNLOCK(&queries);
01040       return CLI_SHOWUSAGE;
01041    }
01042 
01043    if (ast_strlen_zero(query->sql_read)) {
01044       ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
01045       AST_RWLIST_UNLOCK(&queries);
01046       return CLI_SUCCESS;
01047    }
01048 
01049    ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
01050 
01051    /* Evaluate function */
01052    char_args = ast_strdupa(a->argv[3]);
01053 
01054    chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc");
01055 
01056    AST_STANDARD_APP_ARGS(args, char_args);
01057    for (i = 0; i < args.argc; i++) {
01058       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01059       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01060    }
01061 
01062    ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
01063    ast_channel_free(chan);
01064 
01065    if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
01066       /* Execute the query */
01067       struct odbc_obj *obj = NULL;
01068       int dsn, executed = 0;
01069       SQLHSTMT stmt;
01070       int rows = 0, res, x;
01071       SQLSMALLINT colcount = 0, collength;
01072       SQLLEN indicator;
01073       struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
01074       char colname[256];
01075       SQLULEN maxcol;
01076 
01077       for (dsn = 0; dsn < 5; dsn++) {
01078          if (ast_strlen_zero(query->readhandle[dsn])) {
01079             continue;
01080          }
01081          ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
01082          if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) {
01083             continue;
01084          }
01085 
01086          ast_debug(1, "Got obj\n");
01087          if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01088             ast_odbc_release_obj(obj);
01089             obj = NULL;
01090             continue;
01091          }
01092 
01093          executed = 1;
01094 
01095          res = SQLNumResultCols(stmt, &colcount);
01096          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01097             ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
01098             SQLCloseCursor(stmt);
01099             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01100             ast_odbc_release_obj(obj);
01101             obj = NULL;
01102             AST_RWLIST_UNLOCK(&queries);
01103             return CLI_SUCCESS;
01104          }
01105 
01106          res = SQLFetch(stmt);
01107          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01108             SQLCloseCursor(stmt);
01109             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01110             ast_odbc_release_obj(obj);
01111             obj = NULL;
01112             if (res == SQL_NO_DATA) {
01113                ast_cli(a->fd, "Returned %d rows.  Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql));
01114                break;
01115             } else {
01116                ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
01117             }
01118             AST_RWLIST_UNLOCK(&queries);
01119             return CLI_SUCCESS;
01120          }
01121          for (;;) {
01122             for (x = 0; x < colcount; x++) {
01123                res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
01124                if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
01125                   snprintf(colname, sizeof(colname), "field%d", x);
01126                }
01127 
01128                res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
01129                if (indicator == SQL_NULL_DATA) {
01130                   ast_str_set(&coldata, 0, "(nil)");
01131                   res = SQL_SUCCESS;
01132                }
01133 
01134                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01135                   ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
01136                   SQLCloseCursor(stmt);
01137                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01138                   ast_odbc_release_obj(obj);
01139                   obj = NULL;
01140                   AST_RWLIST_UNLOCK(&queries);
01141                   return CLI_SUCCESS;
01142                }
01143 
01144                ast_cli(a->fd, "%-20.20s  %s\n", colname, ast_str_buffer(coldata));
01145             }
01146             rows++;
01147 
01148             /* Get next row */
01149             res = SQLFetch(stmt);
01150             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01151                break;
01152             }
01153             ast_cli(a->fd, "%-20.20s  %s\n", "----------", "----------");
01154          }
01155          SQLCloseCursor(stmt);
01156          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01157          ast_odbc_release_obj(obj);
01158          obj = NULL;
01159          ast_cli(a->fd, "Returned %d row%s.  Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
01160          break;
01161       }
01162       if (obj) {
01163          ast_odbc_release_obj(obj);
01164          obj = NULL;
01165       }
01166 
01167       if (!executed) {
01168          ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
01169       }
01170    } else { /* No execution, just print out the resulting SQL */
01171       ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01172    }
01173    AST_RWLIST_UNLOCK(&queries);
01174    return CLI_SUCCESS;
01175 }
01176 
01177 static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01178 {
01179    AST_DECLARE_APP_ARGS(values,
01180       AST_APP_ARG(field)[100];
01181    );
01182    AST_DECLARE_APP_ARGS(args,
01183       AST_APP_ARG(field)[100];
01184    );
01185    struct ast_str *sql;
01186    char *char_args, *char_values, varname[10];
01187    struct acf_odbc_query *query;
01188    struct ast_channel *chan;
01189    int i;
01190 
01191    switch (cmd) {
01192    case CLI_INIT:
01193       e->command = "odbc write";
01194       e->usage =
01195          "Usage: odbc write <name> <args> <value> [exec]\n"
01196          "       Evaluates the SQL provided in the ODBC function <name>, and\n"
01197          "       optionally executes the function.  This function is intended for\n"
01198          "       testing purposes.  Remember to quote arguments containing spaces.\n";
01199       return NULL;
01200    case CLI_GENERATE:
01201       if (a->pos == 2) {
01202          int wordlen = strlen(a->word), which = 0;
01203          /* Complete function name */
01204          AST_RWLIST_RDLOCK(&queries);
01205          AST_RWLIST_TRAVERSE(&queries, query, list) {
01206             if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01207                if (++which > a->n) {
01208                   char *res = ast_strdup(query->acf->name);
01209                   AST_RWLIST_UNLOCK(&queries);
01210                   return res;
01211                }
01212             }
01213          }
01214          AST_RWLIST_UNLOCK(&queries);
01215          return NULL;
01216       } else if (a->pos == 5) {
01217          return a->n == 0 ? ast_strdup("exec") : NULL;
01218       } else {
01219          return NULL;
01220       }
01221    }
01222 
01223    if (a->argc < 5 || a->argc > 6) {
01224       return CLI_SHOWUSAGE;
01225    }
01226 
01227    sql = ast_str_thread_get(&sql_buf, 16);
01228    if (!sql) {
01229       return CLI_FAILURE;
01230    }
01231 
01232    AST_RWLIST_RDLOCK(&queries);
01233    AST_RWLIST_TRAVERSE(&queries, query, list) {
01234       if (!strcmp(query->acf->name, a->argv[2])) {
01235          break;
01236       }
01237    }
01238 
01239    if (!query) {
01240       ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01241       AST_RWLIST_UNLOCK(&queries);
01242       return CLI_SHOWUSAGE;
01243    }
01244 
01245    if (ast_strlen_zero(query->sql_write)) {
01246       ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
01247       AST_RWLIST_UNLOCK(&queries);
01248       return CLI_SUCCESS;
01249    }
01250 
01251    ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
01252 
01253    /* Evaluate function */
01254    char_args = ast_strdupa(a->argv[3]);
01255    char_values = ast_strdupa(a->argv[4]);
01256 
01257    chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc");
01258 
01259    AST_STANDARD_APP_ARGS(args, char_args);
01260    for (i = 0; i < args.argc; i++) {
01261       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01262       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01263    }
01264 
01265    /* Parse values, just like arguments */
01266    AST_STANDARD_APP_ARGS(values, char_values);
01267    for (i = 0; i < values.argc; i++) {
01268       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
01269       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
01270    }
01271 
01272    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
01273    pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
01274    ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
01275    ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
01276    ast_channel_free(chan);
01277 
01278    if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
01279       /* Execute the query */
01280       struct odbc_obj *obj = NULL;
01281       int dsn, executed = 0;
01282       SQLHSTMT stmt;
01283       SQLLEN rows = -1;
01284 
01285       for (dsn = 0; dsn < 5; dsn++) {
01286          if (ast_strlen_zero(query->writehandle[dsn])) {
01287             continue;
01288          }
01289          if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) {
01290             continue;
01291          }
01292          if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01293             ast_odbc_release_obj(obj);
01294             obj = NULL;
01295             continue;
01296          }
01297 
01298          SQLRowCount(stmt, &rows);
01299          SQLCloseCursor(stmt);
01300          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01301          ast_odbc_release_obj(obj);
01302          obj = NULL;
01303          ast_cli(a->fd, "Affected %d rows.  Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]);
01304          executed = 1;
01305          break;
01306       }
01307 
01308       if (!executed) {
01309          ast_cli(a->fd, "Failed to execute query.\n");
01310       }
01311    } else { /* No execution, just print out the resulting SQL */
01312       ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01313    }
01314    AST_RWLIST_UNLOCK(&queries);
01315    return CLI_SUCCESS;
01316 }
01317 
01318 static struct ast_cli_entry cli_func_odbc[] = {
01319    AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
01320    AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
01321 };
01322 
01323 static int load_module(void)
01324 {
01325    int res = 0;
01326    struct ast_config *cfg;
01327    char *catg;
01328    struct ast_flags config_flags = { 0 };
01329 
01330    res |= ast_custom_function_register(&fetch_function);
01331    res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
01332    AST_RWLIST_WRLOCK(&queries);
01333 
01334    cfg = ast_config_load(config, config_flags);
01335    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
01336       ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
01337       AST_RWLIST_UNLOCK(&queries);
01338       return AST_MODULE_LOAD_DECLINE;
01339    }
01340 
01341    for (catg = ast_category_browse(cfg, NULL);
01342         catg;
01343         catg = ast_category_browse(cfg, catg)) {
01344       struct acf_odbc_query *query = NULL;
01345       int err;
01346 
01347       if ((err = init_acf_query(cfg, catg, &query))) {
01348          if (err == ENOMEM)
01349             ast_log(LOG_ERROR, "Out of memory\n");
01350          else if (err == EINVAL)
01351             ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
01352          else
01353             ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
01354       } else {
01355          AST_RWLIST_INSERT_HEAD(&queries, query, list);
01356          ast_custom_function_register(query->acf);
01357       }
01358    }
01359 
01360    ast_config_destroy(cfg);
01361    res |= ast_custom_function_register(&escape_function);
01362    ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01363 
01364    AST_RWLIST_UNLOCK(&queries);
01365    return res;
01366 }
01367 
01368 static int unload_module(void)
01369 {
01370    struct acf_odbc_query *query;
01371    int res = 0;
01372 
01373    AST_RWLIST_WRLOCK(&queries);
01374    while (!AST_RWLIST_EMPTY(&queries)) {
01375       query = AST_RWLIST_REMOVE_HEAD(&queries, list);
01376       ast_custom_function_unregister(query->acf);
01377       free_acf_query(query);
01378    }
01379 
01380    res |= ast_custom_function_unregister(&escape_function);
01381    res |= ast_custom_function_unregister(&fetch_function);
01382    res |= ast_unregister_application(app_odbcfinish);
01383    ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01384 
01385    /* Allow any threads waiting for this lock to pass (avoids a race) */
01386    AST_RWLIST_UNLOCK(&queries);
01387    usleep(1);
01388    AST_RWLIST_WRLOCK(&queries);
01389 
01390    AST_RWLIST_UNLOCK(&queries);
01391    return 0;
01392 }
01393 
01394 static int reload(void)
01395 {
01396    int res = 0;
01397    struct ast_config *cfg;
01398    struct acf_odbc_query *oldquery;
01399    char *catg;
01400    struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
01401 
01402    cfg = ast_config_load(config, config_flags);
01403    if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
01404       return 0;
01405 
01406    AST_RWLIST_WRLOCK(&queries);
01407 
01408    while (!AST_RWLIST_EMPTY(&queries)) {
01409       oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
01410       ast_custom_function_unregister(oldquery->acf);
01411       free_acf_query(oldquery);
01412    }
01413 
01414    if (!cfg) {
01415       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
01416       goto reload_out;
01417    }
01418 
01419    for (catg = ast_category_browse(cfg, NULL);
01420         catg;
01421         catg = ast_category_browse(cfg, catg)) {
01422       struct acf_odbc_query *query = NULL;
01423 
01424       if (init_acf_query(cfg, catg, &query)) {
01425          ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
01426       } else {
01427          AST_RWLIST_INSERT_HEAD(&queries, query, list);
01428          ast_custom_function_register(query->acf);
01429       }
01430    }
01431 
01432    ast_config_destroy(cfg);
01433 reload_out:
01434    AST_RWLIST_UNLOCK(&queries);
01435    return res;
01436 }
01437 
01438 /* XXX need to revise usecount - set if query_lock is set */
01439 
01440 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
01441       .load = load_module,
01442       .unload = unload_module,
01443       .reload = reload,
01444           );
01445