Sat Apr 26 2014 22:01:37

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