Tue Aug 24 2010 19:41:30

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: 267670 $")
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 || !insertbuf) {
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          obj = NULL;
00325       }
00326    }
00327 
00328    if (stmt && rows == 0 && ast_str_strlen(insertbuf) != 0) {
00329       SQLCloseCursor(stmt);
00330       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00331       for (dsn = 0; dsn < 5; dsn++) {
00332          if (!ast_strlen_zero(query->writehandle[dsn])) {
00333             obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00334             if (obj) {
00335                stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
00336             }
00337          }
00338          if (stmt) {
00339             status = "FAILOVER";
00340             SQLRowCount(stmt, &rows);
00341             break;
00342          }
00343          if (obj) {
00344             ast_odbc_release_obj(obj);
00345             obj = NULL;
00346          }
00347       }
00348    } else if (stmt) {
00349       status = "SUCCESS";
00350       SQLRowCount(stmt, &rows);
00351    }
00352 
00353    AST_RWLIST_UNLOCK(&queries);
00354 
00355    /* Output the affected rows, for all cases.  In the event of failure, we
00356     * flag this as -1 rows.  Note that this is different from 0 affected rows
00357     * which would be the case if we succeeded in our query, but the values did
00358     * not change. */
00359    snprintf(varname, sizeof(varname), "%d", (int)rows);
00360    pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00361    pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00362 
00363    if (stmt) {
00364       SQLCloseCursor(stmt);
00365       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00366    }
00367    if (obj && !transactional) {
00368       ast_odbc_release_obj(obj);
00369       obj = NULL;
00370    }
00371 
00372    if (chan)
00373       ast_autoservice_stop(chan);
00374    if (bogus_chan)
00375       ast_channel_free(chan);
00376 
00377    return 0;
00378 }
00379 
00380 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
00381 {
00382    struct odbc_obj *obj = NULL;
00383    struct acf_odbc_query *query;
00384    char varname[15], rowcount[12] = "-1";
00385    struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
00386    int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
00387    AST_DECLARE_APP_ARGS(args,
00388       AST_APP_ARG(field)[100];
00389    );
00390    SQLHSTMT stmt = NULL;
00391    SQLSMALLINT colcount=0;
00392    SQLLEN indicator;
00393    SQLSMALLINT collength;
00394    struct odbc_datastore *resultset = NULL;
00395    struct odbc_datastore_row *row = NULL;
00396    struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
00397    const char *status = "FAILURE";
00398 
00399    if (!sql || !colnames) {
00400       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00401       return -1;
00402    }
00403 
00404    ast_str_reset(colnames);
00405 
00406    AST_RWLIST_RDLOCK(&queries);
00407    AST_RWLIST_TRAVERSE(&queries, query, list) {
00408       if (!strcmp(query->acf->name, cmd)) {
00409          break;
00410       }
00411    }
00412 
00413    if (!query) {
00414       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00415       AST_RWLIST_UNLOCK(&queries);
00416       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00417       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00418       return -1;
00419    }
00420 
00421    if (!chan) {
00422       if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc"))) {
00423          bogus_chan = 1;
00424       }
00425    }
00426 
00427    if (chan) {
00428       ast_autoservice_start(chan);
00429    }
00430 
00431    AST_STANDARD_APP_ARGS(args, s);
00432    for (x = 0; x < args.argc; x++) {
00433       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00434       pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00435    }
00436 
00437    ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
00438 
00439    /* Restore prior values */
00440    for (x = 0; x < args.argc; x++) {
00441       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00442       pbx_builtin_setvar_helper(chan, varname, NULL);
00443    }
00444 
00445    /* Save these flags, so we can release the lock */
00446    escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00447    if (ast_test_flag(query, OPT_MULTIROW)) {
00448       resultset = ast_calloc(1, sizeof(*resultset));
00449       AST_LIST_HEAD_INIT(resultset);
00450       if (query->rowlimit) {
00451          rowlimit = query->rowlimit;
00452       } else {
00453          rowlimit = INT_MAX;
00454       }
00455    }
00456    AST_RWLIST_UNLOCK(&queries);
00457 
00458    for (dsn = 0; dsn < 5; dsn++) {
00459       if (!ast_strlen_zero(query->readhandle[dsn])) {
00460          obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
00461          if (obj) {
00462             stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql));
00463          }
00464       }
00465       if (stmt) {
00466          break;
00467       }
00468       if (obj) {
00469          ast_odbc_release_obj(obj);
00470          obj = NULL;
00471       }
00472    }
00473 
00474    if (!stmt) {
00475       ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
00476       if (obj) {
00477          ast_odbc_release_obj(obj);
00478          obj = NULL;
00479       }
00480       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00481       if (chan) {
00482          ast_autoservice_stop(chan);
00483       }
00484       if (bogus_chan) {
00485          ast_channel_free(chan);
00486       }
00487       return -1;
00488    }
00489 
00490    res = SQLNumResultCols(stmt, &colcount);
00491    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00492       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
00493       SQLCloseCursor(stmt);
00494       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00495       ast_odbc_release_obj(obj);
00496       obj = NULL;
00497       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00498       if (chan) {
00499          ast_autoservice_stop(chan);
00500       }
00501       if (bogus_chan) {
00502          ast_channel_free(chan);
00503       }
00504       return -1;
00505    }
00506 
00507    res = SQLFetch(stmt);
00508    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00509       int res1 = -1;
00510       if (res == SQL_NO_DATA) {
00511          ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
00512          res1 = 0;
00513          buf[0] = '\0';
00514          ast_copy_string(rowcount, "0", sizeof(rowcount));
00515          status = "NODATA";
00516       } else {
00517          ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
00518          status = "FETCHERROR";
00519       }
00520       SQLCloseCursor(stmt);
00521       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00522       ast_odbc_release_obj(obj);
00523       obj = NULL;
00524       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00525       pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00526       if (chan)
00527          ast_autoservice_stop(chan);
00528       if (bogus_chan)
00529          ast_channel_free(chan);
00530       return res1;
00531    }
00532 
00533    status = "SUCCESS";
00534 
00535    for (y = 0; y < rowlimit; y++) {
00536       buf[0] = '\0';
00537       for (x = 0; x < colcount; x++) {
00538          int i;
00539          struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00540          char *ptrcoldata;
00541 
00542          if (!coldata) {
00543             ast_free(resultset);
00544             SQLCloseCursor(stmt);
00545             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00546             ast_odbc_release_obj(obj);
00547             obj = NULL;
00548             pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00549             if (chan)
00550                ast_autoservice_stop(chan);
00551             if (bogus_chan) {
00552                ast_channel_free(chan);
00553             }
00554             return -1;
00555          }
00556 
00557          if (y == 0) {
00558             char colname[256];
00559             SQLULEN maxcol;
00560 
00561             res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00562             ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00563             if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00564                snprintf(colname, sizeof(colname), "field%d", x);
00565             }
00566 
00567             ast_str_make_space(&coldata, maxcol + 1);
00568 
00569             if (ast_str_strlen(colnames)) {
00570                ast_str_append(&colnames, 0, ",");
00571             }
00572             ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
00573 
00574             if (resultset) {
00575                void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
00576                if (!tmp) {
00577                   ast_log(LOG_ERROR, "No space for a new resultset?\n");
00578                   ast_free(resultset);
00579                   SQLCloseCursor(stmt);
00580                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00581                   ast_odbc_release_obj(obj);
00582                   obj = NULL;
00583                   pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00584                   pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00585                   if (chan)
00586                      ast_autoservice_stop(chan);
00587                   if (bogus_chan)
00588                      ast_channel_free(chan);
00589                   return -1;
00590                }
00591                resultset = tmp;
00592                strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
00593             }
00594          }
00595 
00596          buflen = strlen(buf);
00597          res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
00598          if (indicator == SQL_NULL_DATA) {
00599             ast_debug(3, "Got NULL data\n");
00600             ast_str_reset(coldata);
00601             res = SQL_SUCCESS;
00602          }
00603 
00604          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00605             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
00606             y = -1;
00607             buf[0] = '\0';
00608             goto end_acf_read;
00609          }
00610 
00611          ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
00612 
00613          if (x) {
00614             buf[buflen++] = ',';
00615          }
00616 
00617          /* Copy data, encoding '\' and ',' for the argument parser */
00618          ptrcoldata = ast_str_buffer(coldata);
00619          for (i = 0; i < ast_str_strlen(coldata); i++) {
00620             if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
00621                buf[buflen++] = '\\';
00622             }
00623             buf[buflen++] = ptrcoldata[i];
00624 
00625             if (buflen >= len - 2) {
00626                break;
00627             }
00628 
00629             if (ptrcoldata[i] == '\0') {
00630                break;
00631             }
00632          }
00633 
00634          buf[buflen] = '\0';
00635          ast_debug(2, "buf is now set to '%s'\n", buf);
00636       }
00637       ast_debug(2, "buf is now set to '%s'\n", buf);
00638 
00639       if (resultset) {
00640          row = ast_calloc(1, sizeof(*row) + buflen + 1);
00641          if (!row) {
00642             ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00643             status = "MEMERROR";
00644             goto end_acf_read;
00645          }
00646          strcpy((char *)row + sizeof(*row), buf);
00647          AST_LIST_INSERT_TAIL(resultset, row, list);
00648 
00649          /* Get next row */
00650          res = SQLFetch(stmt);
00651          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00652             if (res != SQL_NO_DATA) {
00653                ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
00654             }
00655             /* Number of rows in the resultset */
00656             y++;
00657             break;
00658          }
00659       }
00660    }
00661 
00662 end_acf_read:
00663    snprintf(rowcount, sizeof(rowcount), "%d", y);
00664    pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00665    pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00666    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
00667    if (resultset) {
00668       int uid;
00669       struct ast_datastore *odbc_store;
00670       uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00671       snprintf(buf, len, "%d", uid);
00672       odbc_store = ast_datastore_alloc(&odbc_info, buf);
00673       if (!odbc_store) {
00674          ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel.  Results fail.\n");
00675          pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00676          odbc_datastore_free(resultset);
00677          SQLCloseCursor(stmt);
00678          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00679          ast_odbc_release_obj(obj);
00680          obj = NULL;
00681          if (chan)
00682             ast_autoservice_stop(chan);
00683          if (bogus_chan)
00684             ast_channel_free(chan);
00685          return -1;
00686       }
00687       odbc_store->data = resultset;
00688       ast_channel_datastore_add(chan, odbc_store);
00689    }
00690    SQLCloseCursor(stmt);
00691    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00692    ast_odbc_release_obj(obj);
00693    obj = NULL;
00694    if (chan)
00695       ast_autoservice_stop(chan);
00696    if (bogus_chan)
00697       ast_channel_free(chan);
00698    return 0;
00699 }
00700 
00701 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00702 {
00703    char *out = buf;
00704 
00705    for (; *data && out - buf < len; data++) {
00706       if (*data == '\'') {
00707          *out = '\'';
00708          out++;
00709       }
00710       *out++ = *data;
00711    }
00712    *out = '\0';
00713 
00714    return 0;
00715 }
00716 
00717 static struct ast_custom_function escape_function = {
00718    .name = "SQL_ESC",
00719    .read = acf_escape,
00720    .write = NULL,
00721 };
00722 
00723 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00724 {
00725    struct ast_datastore *store;
00726    struct odbc_datastore *resultset;
00727    struct odbc_datastore_row *row;
00728    store = ast_channel_datastore_find(chan, &odbc_info, data);
00729    if (!store) {
00730       pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00731       return -1;
00732    }
00733    resultset = store->data;
00734    AST_LIST_LOCK(resultset);
00735    row = AST_LIST_REMOVE_HEAD(resultset, list);
00736    AST_LIST_UNLOCK(resultset);
00737    if (!row) {
00738       /* Cleanup datastore */
00739       ast_channel_datastore_remove(chan, store);
00740       ast_datastore_free(store);
00741       pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00742       return -1;
00743    }
00744    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00745    ast_copy_string(buf, row->data, len);
00746    ast_free(row);
00747    pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
00748    return 0;
00749 }
00750 
00751 static struct ast_custom_function fetch_function = {
00752    .name = "ODBC_FETCH",
00753    .read = acf_fetch,
00754    .write = NULL,
00755 };
00756 
00757 static char *app_odbcfinish = "ODBCFinish";
00758 
00759 static int exec_odbcfinish(struct ast_channel *chan, void *data)
00760 {
00761    struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00762    if (!store) /* Already freed; no big deal. */
00763       return 0;
00764    ast_channel_datastore_remove(chan, store);
00765    ast_datastore_free(store);
00766    return 0;
00767 }
00768 
00769 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00770 {
00771    const char *tmp;
00772    int i;
00773 
00774    if (!cfg || !catg) {
00775       return EINVAL;
00776    }
00777 
00778    *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00779    if (! (*query))
00780       return ENOMEM;
00781 
00782    if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00783       char *tmp2 = ast_strdupa(tmp);
00784       AST_DECLARE_APP_ARGS(writeconf,
00785          AST_APP_ARG(dsn)[5];
00786       );
00787       AST_STANDARD_APP_ARGS(writeconf, tmp2);
00788       for (i = 0; i < 5; i++) {
00789          if (!ast_strlen_zero(writeconf.dsn[i]))
00790             ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00791       }
00792    }
00793 
00794    if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00795       char *tmp2 = ast_strdupa(tmp);
00796       AST_DECLARE_APP_ARGS(readconf,
00797          AST_APP_ARG(dsn)[5];
00798       );
00799       AST_STANDARD_APP_ARGS(readconf, tmp2);
00800       for (i = 0; i < 5; i++) {
00801          if (!ast_strlen_zero(readconf.dsn[i]))
00802             ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00803       }
00804    } else {
00805       /* If no separate readhandle, then use the writehandle for reading */
00806       for (i = 0; i < 5; i++) {
00807          if (!ast_strlen_zero((*query)->writehandle[i]))
00808             ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00809       }
00810    }
00811 
00812    if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00813       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00814    else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00815       ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
00816       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00817    }
00818 
00819    if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00820       ast_free(*query);
00821       *query = NULL;
00822       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00823       return EINVAL;
00824    }
00825 
00826    if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00827       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00828    else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00829       ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
00830       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00831    }
00832 
00833    if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00834       ast_free(*query);
00835       *query = NULL;
00836       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00837       return EINVAL;
00838    }
00839 
00840    if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
00841       ast_copy_string((*query)->sql_insert, tmp, sizeof((*query)->sql_insert));
00842    }
00843 
00844    /* Allow escaping of embedded commas in fields to be turned off */
00845    ast_set_flag((*query), OPT_ESCAPECOMMAS);
00846    if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00847       if (ast_false(tmp))
00848          ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00849    }
00850 
00851    if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00852       if (strcasecmp(tmp, "multirow") == 0)
00853          ast_set_flag((*query), OPT_MULTIROW);
00854       if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00855          sscanf(tmp, "%30d", &((*query)->rowlimit));
00856    }
00857 
00858    (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00859    if (! (*query)->acf) {
00860       ast_free(*query);
00861       *query = NULL;
00862       return ENOMEM;
00863    }
00864    if (ast_string_field_init((*query)->acf, 128)) {
00865       ast_free((*query)->acf);
00866       ast_free(*query);
00867       *query = NULL;
00868       return ENOMEM;
00869    }
00870 
00871    if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00872       if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00873          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00874       }
00875    } else {
00876       if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00877          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00878       }
00879    }
00880 
00881    if (!((*query)->acf->name)) {
00882       ast_string_field_free_memory((*query)->acf);
00883       ast_free((*query)->acf);
00884       ast_free(*query);
00885       *query = NULL;
00886       return ENOMEM;
00887    }
00888 
00889    if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
00890       ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
00891    } else {
00892       ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
00893    }
00894 
00895    if (ast_strlen_zero((*query)->acf->syntax)) {
00896       ast_free((char *)(*query)->acf->name);
00897       ast_string_field_free_memory((*query)->acf);
00898       ast_free((*query)->acf);
00899       ast_free(*query);
00900       *query = NULL;
00901       return ENOMEM;
00902    }
00903 
00904    if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
00905       ast_string_field_set((*query)->acf, synopsis, tmp);
00906    } else {
00907       ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
00908    }
00909 
00910    if (ast_strlen_zero((*query)->acf->synopsis)) {
00911       ast_free((char *)(*query)->acf->name);
00912       ast_string_field_free_memory((*query)->acf);
00913       ast_free((*query)->acf);
00914       ast_free(*query);
00915       *query = NULL;
00916       return ENOMEM;
00917    }
00918 
00919    if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00920       ast_string_field_build((*query)->acf, desc,
00921                "Runs the following query, as defined in func_odbc.conf, performing\n"
00922                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00923                "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
00924                "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00925                "%s"
00926                "\nRead:\n%s\n\nWrite:\n%s\n%s%s%s",
00927                ast_strlen_zero((*query)->sql_insert) ? "" :
00928                   "If the write query affects no rows, the insert query will be\n"
00929                   "performed.\n",
00930                (*query)->sql_read,
00931                (*query)->sql_write,
00932                ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
00933                ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
00934                ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
00935    } else if (!ast_strlen_zero((*query)->sql_read)) {
00936       ast_string_field_build((*query)->acf, desc,
00937                   "Runs the following query, as defined in func_odbc.conf, performing\n"
00938                      "substitution of the arguments into the query as specified by ${ARG1},\n"
00939                   "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
00940                   (*query)->sql_read);
00941    } else if (!ast_strlen_zero((*query)->sql_write)) {
00942       ast_string_field_build((*query)->acf, desc,  
00943                "Runs the following query, as defined in func_odbc.conf, performing\n"
00944                   "substitution of the arguments into the query as specified by ${ARG1},\n"
00945                "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
00946                "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00947                "This function may only be set.\n%sSQL:\n%s\n%s%s%s",
00948                ast_strlen_zero((*query)->sql_insert) ? "" :
00949                   "If the write query affects no rows, the insert query will be\n"
00950                   "performed.\n",
00951                (*query)->sql_write,
00952                ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
00953                ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
00954                ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
00955    } else {
00956       ast_string_field_free_memory((*query)->acf);
00957       ast_free((char *)(*query)->acf->name);
00958       ast_free((*query)->acf);
00959       ast_free(*query);
00960       ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute.  Ignoring.\n", catg);
00961       return EINVAL;
00962    }
00963 
00964    if (ast_strlen_zero((*query)->acf->desc)) {
00965       ast_string_field_free_memory((*query)->acf);
00966       ast_free((char *)(*query)->acf->name);
00967       ast_free((*query)->acf);
00968       ast_free(*query);
00969       *query = NULL;
00970       return ENOMEM;
00971    }
00972 
00973    if (ast_strlen_zero((*query)->sql_read)) {
00974       (*query)->acf->read = NULL;
00975    } else {
00976       (*query)->acf->read = acf_odbc_read;
00977    }
00978 
00979    if (ast_strlen_zero((*query)->sql_write)) {
00980       (*query)->acf->write = NULL;
00981    } else {
00982       (*query)->acf->write = acf_odbc_write;
00983    }
00984 
00985    return 0;
00986 }
00987 
00988 static int free_acf_query(struct acf_odbc_query *query)
00989 {
00990    if (query) {
00991       if (query->acf) {
00992          if (query->acf->name)
00993             ast_free((char *)query->acf->name);
00994          ast_string_field_free_memory(query->acf);
00995          ast_free(query->acf);
00996       }
00997       ast_free(query);
00998    }
00999    return 0;
01000 }
01001 
01002 static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01003 {
01004    AST_DECLARE_APP_ARGS(args,
01005       AST_APP_ARG(field)[100];
01006    );
01007    struct ast_str *sql;
01008    char *char_args, varname[10];
01009    struct acf_odbc_query *query;
01010    struct ast_channel *chan;
01011    int i;
01012 
01013    switch (cmd) {
01014    case CLI_INIT:
01015       e->command = "odbc read";
01016       e->usage =
01017          "Usage: odbc read <name> <args> [exec]\n"
01018          "       Evaluates the SQL provided in the ODBC function <name>, and\n"
01019          "       optionally executes the function.  This function is intended for\n"
01020          "       testing purposes.  Remember to quote arguments containing spaces.\n";
01021       return NULL;
01022    case CLI_GENERATE:
01023       if (a->pos == 2) {
01024          int wordlen = strlen(a->word), which = 0;
01025          /* Complete function name */
01026          AST_RWLIST_RDLOCK(&queries);
01027          AST_RWLIST_TRAVERSE(&queries, query, list) {
01028             if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01029                if (++which > a->n) {
01030                   char *res = ast_strdup(query->acf->name);
01031                   AST_RWLIST_UNLOCK(&queries);
01032                   return res;
01033                }
01034             }
01035          }
01036          AST_RWLIST_UNLOCK(&queries);
01037          return NULL;
01038       } else if (a->pos == 4) {
01039          return a->n == 0 ? ast_strdup("exec") : NULL;
01040       } else {
01041          return NULL;
01042       }
01043    }
01044 
01045    if (a->argc < 4 || a->argc > 5) {
01046       return CLI_SHOWUSAGE;
01047    }
01048 
01049    sql = ast_str_thread_get(&sql_buf, 16);
01050    if (!sql) {
01051       return CLI_FAILURE;
01052    }
01053 
01054    AST_RWLIST_RDLOCK(&queries);
01055    AST_RWLIST_TRAVERSE(&queries, query, list) {
01056       if (!strcmp(query->acf->name, a->argv[2])) {
01057          break;
01058       }
01059    }
01060 
01061    if (!query) {
01062       ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01063       AST_RWLIST_UNLOCK(&queries);
01064       return CLI_SHOWUSAGE;
01065    }
01066 
01067    if (ast_strlen_zero(query->sql_read)) {
01068       ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
01069       AST_RWLIST_UNLOCK(&queries);
01070       return CLI_SUCCESS;
01071    }
01072 
01073    ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
01074 
01075    /* Evaluate function */
01076    char_args = ast_strdupa(a->argv[3]);
01077 
01078    chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc");
01079 
01080    AST_STANDARD_APP_ARGS(args, char_args);
01081    for (i = 0; i < args.argc; i++) {
01082       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01083       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01084    }
01085 
01086    ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
01087    ast_channel_free(chan);
01088 
01089    if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
01090       /* Execute the query */
01091       struct odbc_obj *obj = NULL;
01092       int dsn, executed = 0;
01093       SQLHSTMT stmt;
01094       int rows = 0, res, x;
01095       SQLSMALLINT colcount = 0, collength;
01096       SQLLEN indicator;
01097       struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
01098       char colname[256];
01099       SQLULEN maxcol;
01100 
01101       if (!coldata) {
01102          AST_RWLIST_UNLOCK(&queries);
01103          return CLI_SUCCESS;
01104       }
01105 
01106       for (dsn = 0; dsn < 5; dsn++) {
01107          if (ast_strlen_zero(query->readhandle[dsn])) {
01108             continue;
01109          }
01110          ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
01111          if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) {
01112             continue;
01113          }
01114 
01115          ast_debug(1, "Got obj\n");
01116          if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01117             ast_odbc_release_obj(obj);
01118             obj = NULL;
01119             continue;
01120          }
01121 
01122          executed = 1;
01123 
01124          res = SQLNumResultCols(stmt, &colcount);
01125          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01126             ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
01127             SQLCloseCursor(stmt);
01128             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01129             ast_odbc_release_obj(obj);
01130             obj = NULL;
01131             AST_RWLIST_UNLOCK(&queries);
01132             return CLI_SUCCESS;
01133          }
01134 
01135          res = SQLFetch(stmt);
01136          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01137             SQLCloseCursor(stmt);
01138             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01139             ast_odbc_release_obj(obj);
01140             obj = NULL;
01141             if (res == SQL_NO_DATA) {
01142                ast_cli(a->fd, "Returned %d rows.  Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql));
01143                break;
01144             } else {
01145                ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
01146             }
01147             AST_RWLIST_UNLOCK(&queries);
01148             return CLI_SUCCESS;
01149          }
01150          for (;;) {
01151             for (x = 0; x < colcount; x++) {
01152                res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
01153                if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
01154                   snprintf(colname, sizeof(colname), "field%d", x);
01155                }
01156 
01157                res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
01158                if (indicator == SQL_NULL_DATA) {
01159                   ast_str_set(&coldata, 0, "(nil)");
01160                   res = SQL_SUCCESS;
01161                }
01162 
01163                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01164                   ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
01165                   SQLCloseCursor(stmt);
01166                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01167                   ast_odbc_release_obj(obj);
01168                   obj = NULL;
01169                   AST_RWLIST_UNLOCK(&queries);
01170                   return CLI_SUCCESS;
01171                }
01172 
01173                ast_cli(a->fd, "%-20.20s  %s\n", colname, ast_str_buffer(coldata));
01174             }
01175             rows++;
01176 
01177             /* Get next row */
01178             res = SQLFetch(stmt);
01179             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01180                break;
01181             }
01182             ast_cli(a->fd, "%-20.20s  %s\n", "----------", "----------");
01183          }
01184          SQLCloseCursor(stmt);
01185          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01186          ast_odbc_release_obj(obj);
01187          obj = NULL;
01188          ast_cli(a->fd, "Returned %d row%s.  Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
01189          break;
01190       }
01191       if (obj) {
01192          ast_odbc_release_obj(obj);
01193          obj = NULL;
01194       }
01195 
01196       if (!executed) {
01197          ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
01198       }
01199    } else { /* No execution, just print out the resulting SQL */
01200       ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01201    }
01202    AST_RWLIST_UNLOCK(&queries);
01203    return CLI_SUCCESS;
01204 }
01205 
01206 static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01207 {
01208    AST_DECLARE_APP_ARGS(values,
01209       AST_APP_ARG(field)[100];
01210    );
01211    AST_DECLARE_APP_ARGS(args,
01212       AST_APP_ARG(field)[100];
01213    );
01214    struct ast_str *sql;
01215    char *char_args, *char_values, varname[10];
01216    struct acf_odbc_query *query;
01217    struct ast_channel *chan;
01218    int i;
01219 
01220    switch (cmd) {
01221    case CLI_INIT:
01222       e->command = "odbc write";
01223       e->usage =
01224          "Usage: odbc write <name> <args> <value> [exec]\n"
01225          "       Evaluates the SQL provided in the ODBC function <name>, and\n"
01226          "       optionally executes the function.  This function is intended for\n"
01227          "       testing purposes.  Remember to quote arguments containing spaces.\n";
01228       return NULL;
01229    case CLI_GENERATE:
01230       if (a->pos == 2) {
01231          int wordlen = strlen(a->word), which = 0;
01232          /* Complete function name */
01233          AST_RWLIST_RDLOCK(&queries);
01234          AST_RWLIST_TRAVERSE(&queries, query, list) {
01235             if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01236                if (++which > a->n) {
01237                   char *res = ast_strdup(query->acf->name);
01238                   AST_RWLIST_UNLOCK(&queries);
01239                   return res;
01240                }
01241             }
01242          }
01243          AST_RWLIST_UNLOCK(&queries);
01244          return NULL;
01245       } else if (a->pos == 5) {
01246          return a->n == 0 ? ast_strdup("exec") : NULL;
01247       } else {
01248          return NULL;
01249       }
01250    }
01251 
01252    if (a->argc < 5 || a->argc > 6) {
01253       return CLI_SHOWUSAGE;
01254    }
01255 
01256    sql = ast_str_thread_get(&sql_buf, 16);
01257    if (!sql) {
01258       return CLI_FAILURE;
01259    }
01260 
01261    AST_RWLIST_RDLOCK(&queries);
01262    AST_RWLIST_TRAVERSE(&queries, query, list) {
01263       if (!strcmp(query->acf->name, a->argv[2])) {
01264          break;
01265       }
01266    }
01267 
01268    if (!query) {
01269       ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01270       AST_RWLIST_UNLOCK(&queries);
01271       return CLI_SHOWUSAGE;
01272    }
01273 
01274    if (ast_strlen_zero(query->sql_write)) {
01275       ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
01276       AST_RWLIST_UNLOCK(&queries);
01277       return CLI_SUCCESS;
01278    }
01279 
01280    ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
01281 
01282    /* Evaluate function */
01283    char_args = ast_strdupa(a->argv[3]);
01284    char_values = ast_strdupa(a->argv[4]);
01285 
01286    chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc");
01287 
01288    AST_STANDARD_APP_ARGS(args, char_args);
01289    for (i = 0; i < args.argc; i++) {
01290       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01291       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01292    }
01293 
01294    /* Parse values, just like arguments */
01295    AST_STANDARD_APP_ARGS(values, char_values);
01296    for (i = 0; i < values.argc; i++) {
01297       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
01298       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
01299    }
01300 
01301    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
01302    pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
01303    ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
01304    ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
01305    ast_channel_free(chan);
01306 
01307    if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
01308       /* Execute the query */
01309       struct odbc_obj *obj = NULL;
01310       int dsn, executed = 0;
01311       SQLHSTMT stmt;
01312       SQLLEN rows = -1;
01313 
01314       for (dsn = 0; dsn < 5; dsn++) {
01315          if (ast_strlen_zero(query->writehandle[dsn])) {
01316             continue;
01317          }
01318          if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) {
01319             continue;
01320          }
01321          if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01322             ast_odbc_release_obj(obj);
01323             obj = NULL;
01324             continue;
01325          }
01326 
01327          SQLRowCount(stmt, &rows);
01328          SQLCloseCursor(stmt);
01329          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01330          ast_odbc_release_obj(obj);
01331          obj = NULL;
01332          ast_cli(a->fd, "Affected %d rows.  Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]);
01333          executed = 1;
01334          break;
01335       }
01336 
01337       if (!executed) {
01338          ast_cli(a->fd, "Failed to execute query.\n");
01339       }
01340    } else { /* No execution, just print out the resulting SQL */
01341       ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01342    }
01343    AST_RWLIST_UNLOCK(&queries);
01344    return CLI_SUCCESS;
01345 }
01346 
01347 static struct ast_cli_entry cli_func_odbc[] = {
01348    AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
01349    AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
01350 };
01351 
01352 static int load_module(void)
01353 {
01354    int res = 0;
01355    struct ast_config *cfg;
01356    char *catg;
01357    struct ast_flags config_flags = { 0 };
01358 
01359    res |= ast_custom_function_register(&fetch_function);
01360    res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
01361    AST_RWLIST_WRLOCK(&queries);
01362 
01363    cfg = ast_config_load(config, config_flags);
01364    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
01365       ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
01366       AST_RWLIST_UNLOCK(&queries);
01367       return AST_MODULE_LOAD_DECLINE;
01368    }
01369 
01370    for (catg = ast_category_browse(cfg, NULL);
01371         catg;
01372         catg = ast_category_browse(cfg, catg)) {
01373       struct acf_odbc_query *query = NULL;
01374       int err;
01375 
01376       if ((err = init_acf_query(cfg, catg, &query))) {
01377          if (err == ENOMEM)
01378             ast_log(LOG_ERROR, "Out of memory\n");
01379          else if (err == EINVAL)
01380             ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
01381          else
01382             ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
01383       } else {
01384          AST_RWLIST_INSERT_HEAD(&queries, query, list);
01385          ast_custom_function_register(query->acf);
01386       }
01387    }
01388 
01389    ast_config_destroy(cfg);
01390    res |= ast_custom_function_register(&escape_function);
01391    ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01392 
01393    AST_RWLIST_UNLOCK(&queries);
01394    return res;
01395 }
01396 
01397 static int unload_module(void)
01398 {
01399    struct acf_odbc_query *query;
01400    int res = 0;
01401 
01402    AST_RWLIST_WRLOCK(&queries);
01403    while (!AST_RWLIST_EMPTY(&queries)) {
01404       query = AST_RWLIST_REMOVE_HEAD(&queries, list);
01405       ast_custom_function_unregister(query->acf);
01406       free_acf_query(query);
01407    }
01408 
01409    res |= ast_custom_function_unregister(&escape_function);
01410    res |= ast_custom_function_unregister(&fetch_function);
01411    res |= ast_unregister_application(app_odbcfinish);
01412    ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01413 
01414    /* Allow any threads waiting for this lock to pass (avoids a race) */
01415    AST_RWLIST_UNLOCK(&queries);
01416    usleep(1);
01417    AST_RWLIST_WRLOCK(&queries);
01418 
01419    AST_RWLIST_UNLOCK(&queries);
01420    return 0;
01421 }
01422 
01423 static int reload(void)
01424 {
01425    int res = 0;
01426    struct ast_config *cfg;
01427    struct acf_odbc_query *oldquery;
01428    char *catg;
01429    struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
01430 
01431    cfg = ast_config_load(config, config_flags);
01432    if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
01433       return 0;
01434 
01435    AST_RWLIST_WRLOCK(&queries);
01436 
01437    while (!AST_RWLIST_EMPTY(&queries)) {
01438       oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
01439       ast_custom_function_unregister(oldquery->acf);
01440       free_acf_query(oldquery);
01441    }
01442 
01443    if (!cfg) {
01444       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
01445       goto reload_out;
01446    }
01447 
01448    for (catg = ast_category_browse(cfg, NULL);
01449         catg;
01450         catg = ast_category_browse(cfg, catg)) {
01451       struct acf_odbc_query *query = NULL;
01452 
01453       if (init_acf_query(cfg, catg, &query)) {
01454          ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
01455       } else {
01456          AST_RWLIST_INSERT_HEAD(&queries, query, list);
01457          ast_custom_function_register(query->acf);
01458       }
01459    }
01460 
01461    ast_config_destroy(cfg);
01462 reload_out:
01463    AST_RWLIST_UNLOCK(&queries);
01464    return res;
01465 }
01466 
01467 /* XXX need to revise usecount - set if query_lock is set */
01468 
01469 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
01470       .load = load_module,
01471       .unload = unload_module,
01472       .reload = reload,
01473           );
01474