Mon Mar 12 2012 21:21:17

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