Fri Jul 24 00:40:56 2009

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  *
00006  * Tilghman Lesher <func_odbc__200508@the-tilghman.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*!
00020  * \file
00021  *
00022  * \brief ODBC lookups
00023  *
00024  * \author Tilghman Lesher <func_odbc__200508@the-tilghman.com>
00025  *
00026  * \ingroup functions
00027  */
00028 
00029 /*** MODULEINFO
00030    <depend>unixodbc</depend>
00031    <depend>ltdl</depend>
00032    <depend>res_odbc</depend>
00033  ***/
00034 
00035 #include "asterisk.h"
00036 
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 155396 $")
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 
00047 static char *config = "func_odbc.conf";
00048 
00049 enum {
00050    OPT_ESCAPECOMMAS =   (1 << 0),
00051    OPT_MULTIROW     =   (1 << 1),
00052 } odbc_option_flags;
00053 
00054 struct acf_odbc_query {
00055    AST_RWLIST_ENTRY(acf_odbc_query) list;
00056    char readhandle[5][30];
00057    char writehandle[5][30];
00058    char sql_read[2048];
00059    char sql_write[2048];
00060    unsigned int flags;
00061    int rowlimit;
00062    struct ast_custom_function *acf;
00063 };
00064 
00065 static void odbc_datastore_free(void *data);
00066 
00067 struct ast_datastore_info odbc_info = {
00068    .type = "FUNC_ODBC",
00069    .destroy = odbc_datastore_free,
00070 };
00071 
00072 /* For storing each result row */
00073 struct odbc_datastore_row {
00074    AST_LIST_ENTRY(odbc_datastore_row) list;
00075    char data[0];
00076 };
00077 
00078 /* For storing each result set */
00079 struct odbc_datastore {
00080    AST_LIST_HEAD(, odbc_datastore_row);
00081    char names[0];
00082 };
00083 
00084 AST_RWLIST_HEAD_STATIC(queries, acf_odbc_query);
00085 
00086 static int resultcount = 0;
00087 
00088 AST_THREADSTORAGE(coldata_buf);
00089 AST_THREADSTORAGE(colnames_buf);
00090 
00091 static void odbc_datastore_free(void *data)
00092 {
00093    struct odbc_datastore *result = data;
00094    struct odbc_datastore_row *row;
00095    AST_LIST_LOCK(result);
00096    while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
00097       ast_free(row);
00098    }
00099    AST_LIST_UNLOCK(result);
00100    AST_LIST_HEAD_DESTROY(result);
00101    ast_free(result);
00102 }
00103 
00104 static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
00105 {
00106    int res;
00107    char *sql = data;
00108    SQLHSTMT stmt;
00109 
00110    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00111    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00112       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00113       return NULL;
00114    }
00115 
00116    res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
00117    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00118       ast_log(LOG_WARNING, "SQL Exec Direct failed![%s]\n", sql);
00119       SQLCloseCursor(stmt);
00120       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00121       return NULL;
00122    }
00123 
00124    return stmt;
00125 }
00126 
00127 /*
00128  * Master control routine
00129  */
00130 static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
00131 {
00132    struct odbc_obj *obj = NULL;
00133    struct acf_odbc_query *query;
00134    char *t, varname[15];
00135    int i, dsn, bogus_chan = 0;
00136    AST_DECLARE_APP_ARGS(values,
00137       AST_APP_ARG(field)[100];
00138    );
00139    AST_DECLARE_APP_ARGS(args,
00140       AST_APP_ARG(field)[100];
00141    );
00142    SQLHSTMT stmt = NULL;
00143    SQLLEN rows=0;
00144    struct ast_str *buf = ast_str_create(16);
00145 
00146    if (!buf) {
00147       return -1;
00148    }
00149 
00150    AST_RWLIST_RDLOCK(&queries);
00151    AST_RWLIST_TRAVERSE(&queries, query, list) {
00152       if (!strcmp(query->acf->name, cmd)) {
00153          break;
00154       }
00155    }
00156 
00157    if (!query) {
00158       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00159       AST_RWLIST_UNLOCK(&queries);
00160       ast_free(buf);
00161       return -1;
00162    }
00163 
00164    if (!chan) {
00165       if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00166          bogus_chan = 1;
00167    }
00168 
00169    if (chan)
00170       ast_autoservice_start(chan);
00171 
00172    ast_str_make_space(&buf, strlen(query->sql_write) * 2);
00173 
00174    /* Parse our arguments */
00175    t = value ? ast_strdupa(value) : "";
00176 
00177    if (!s || !t) {
00178       ast_log(LOG_ERROR, "Out of memory\n");
00179       AST_RWLIST_UNLOCK(&queries);
00180       if (chan)
00181          ast_autoservice_stop(chan);
00182       if (bogus_chan)
00183          ast_channel_free(chan);
00184       ast_free(buf);
00185       return -1;
00186    }
00187 
00188    AST_STANDARD_APP_ARGS(args, s);
00189    for (i = 0; i < args.argc; i++) {
00190       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00191       pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00192    }
00193 
00194    /* Parse values, just like arguments */
00195    AST_STANDARD_APP_ARGS(values, t);
00196    for (i = 0; i < values.argc; i++) {
00197       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00198       pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00199    }
00200 
00201    /* Additionally set the value as a whole (but push an empty string if value is NULL) */
00202    pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00203 
00204    pbx_substitute_variables_helper(chan, query->sql_write, buf->str, buf->len - 1);
00205 
00206    /* Restore prior values */
00207    for (i = 0; i < args.argc; i++) {
00208       snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00209       pbx_builtin_setvar_helper(chan, varname, NULL);
00210    }
00211 
00212    for (i = 0; i < values.argc; i++) {
00213       snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00214       pbx_builtin_setvar_helper(chan, varname, NULL);
00215    }
00216    pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00217 
00218    AST_RWLIST_UNLOCK(&queries);
00219 
00220    for (dsn = 0; dsn < 5; dsn++) {
00221       if (!ast_strlen_zero(query->writehandle[dsn])) {
00222          obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00223          if (obj)
00224             stmt = ast_odbc_direct_execute(obj, generic_execute, buf->str);
00225       }
00226       if (stmt)
00227          break;
00228    }
00229 
00230    if (stmt) {
00231       /* Rows affected */
00232       SQLRowCount(stmt, &rows);
00233    }
00234 
00235    /* Output the affected rows, for all cases.  In the event of failure, we
00236     * flag this as -1 rows.  Note that this is different from 0 affected rows
00237     * which would be the case if we succeeded in our query, but the values did
00238     * not change. */
00239    snprintf(varname, sizeof(varname), "%d", (int)rows);
00240    pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00241 
00242    if (stmt) {
00243       SQLCloseCursor(stmt);
00244       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00245    }
00246    if (obj)
00247       ast_odbc_release_obj(obj);
00248 
00249    if (chan)
00250       ast_autoservice_stop(chan);
00251    if (bogus_chan)
00252       ast_channel_free(chan);
00253    ast_free(buf);
00254 
00255    return 0;
00256 }
00257 
00258 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
00259 {
00260    struct odbc_obj *obj = NULL;
00261    struct acf_odbc_query *query;
00262    char varname[15], rowcount[12] = "-1";
00263    struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
00264    int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
00265    AST_DECLARE_APP_ARGS(args,
00266       AST_APP_ARG(field)[100];
00267    );
00268    SQLHSTMT stmt = NULL;
00269    SQLSMALLINT colcount=0;
00270    SQLLEN indicator;
00271    SQLSMALLINT collength;
00272    struct odbc_datastore *resultset = NULL;
00273    struct odbc_datastore_row *row = NULL;
00274    struct ast_str *sql = ast_str_create(16);
00275 
00276    if (!sql) {
00277       return -1;
00278    }
00279 
00280    ast_str_reset(colnames);
00281 
00282    AST_RWLIST_RDLOCK(&queries);
00283    AST_RWLIST_TRAVERSE(&queries, query, list) {
00284       if (!strcmp(query->acf->name, cmd)) {
00285          break;
00286       }
00287    }
00288 
00289    if (!query) {
00290       ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00291       AST_RWLIST_UNLOCK(&queries);
00292       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00293       ast_free(sql);
00294       return -1;
00295    }
00296 
00297    if (!chan) {
00298       if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00299          bogus_chan = 1;
00300    }
00301 
00302    if (chan)
00303       ast_autoservice_start(chan);
00304 
00305    AST_STANDARD_APP_ARGS(args, s);
00306    for (x = 0; x < args.argc; x++) {
00307       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00308       pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00309    }
00310 
00311    ast_str_make_space(&sql, strlen(query->sql_read) * 2);
00312    pbx_substitute_variables_helper(chan, query->sql_read, sql->str, sql->len - 1);
00313 
00314    /* Restore prior values */
00315    for (x = 0; x < args.argc; x++) {
00316       snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00317       pbx_builtin_setvar_helper(chan, varname, NULL);
00318    }
00319 
00320    /* Save these flags, so we can release the lock */
00321    escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00322    if (ast_test_flag(query, OPT_MULTIROW)) {
00323       resultset = ast_calloc(1, sizeof(*resultset));
00324       AST_LIST_HEAD_INIT(resultset);
00325       if (query->rowlimit)
00326          rowlimit = query->rowlimit;
00327       else
00328          rowlimit = INT_MAX;
00329    }
00330    AST_RWLIST_UNLOCK(&queries);
00331 
00332    for (dsn = 0; dsn < 5; dsn++) {
00333       if (!ast_strlen_zero(query->readhandle[dsn])) {
00334          obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
00335          if (obj)
00336             stmt = ast_odbc_direct_execute(obj, generic_execute, sql->str);
00337       }
00338       if (stmt)
00339          break;
00340    }
00341 
00342    if (!stmt) {
00343       ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql->str);
00344       if (obj)
00345          ast_odbc_release_obj(obj);
00346       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00347       if (chan)
00348          ast_autoservice_stop(chan);
00349       if (bogus_chan)
00350          ast_channel_free(chan);
00351       ast_free(sql);
00352       return -1;
00353    }
00354 
00355    res = SQLNumResultCols(stmt, &colcount);
00356    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00357       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql->str);
00358       SQLCloseCursor(stmt);
00359       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00360       ast_odbc_release_obj(obj);
00361       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00362       if (chan)
00363          ast_autoservice_stop(chan);
00364       if (bogus_chan)
00365          ast_channel_free(chan);
00366       ast_free(sql);
00367       return -1;
00368    }
00369 
00370    res = SQLFetch(stmt);
00371    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00372       int res1 = -1;
00373       if (res == SQL_NO_DATA) {
00374          ast_verb(4, "Found no rows [%s]\n", sql->str);
00375          res1 = 0;
00376          buf[0] = '\0';
00377          ast_copy_string(rowcount, "0", sizeof(rowcount));
00378       } else {
00379          ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
00380       }
00381       SQLCloseCursor(stmt);
00382       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00383       ast_odbc_release_obj(obj);
00384       pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00385       if (chan)
00386          ast_autoservice_stop(chan);
00387       if (bogus_chan)
00388          ast_channel_free(chan);
00389       ast_free(sql);
00390       return res1;
00391    }
00392 
00393    for (y = 0; y < rowlimit; y++) {
00394       for (x = 0; x < colcount; x++) {
00395          int i;
00396          struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00397 
00398          if (y == 0) {
00399             char colname[256];
00400             SQLULEN maxcol;
00401 
00402             res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00403             ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00404             if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00405                snprintf(colname, sizeof(colname), "field%d", x);
00406             }
00407 
00408             if (coldata->len < maxcol + 1) {
00409                ast_str_make_space(&coldata, maxcol + 1);
00410             }
00411 
00412             if (colnames->used) {
00413                ast_str_append(&colnames, 0, ",");
00414             }
00415             ast_str_make_space(&colnames, strlen(colname) * 2 + 1 + colnames->used);
00416 
00417             /* Copy data, encoding '\' and ',' for the argument parser */
00418             for (i = 0; i < sizeof(colname); i++) {
00419                if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
00420                   colnames->str[colnames->used++] = '\\';
00421                }
00422                colnames->str[colnames->used++] = colname[i];
00423 
00424                if (colname[i] == '\0') {
00425                   colnames->used--;
00426                   break;
00427                }
00428             }
00429 
00430             if (resultset) {
00431                void *tmp = ast_realloc(resultset, sizeof(*resultset) + colnames->used + 1);
00432                if (!tmp) {
00433                   ast_log(LOG_ERROR, "No space for a new resultset?\n");
00434                   ast_free(resultset);
00435                   SQLCloseCursor(stmt);
00436                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00437                   ast_odbc_release_obj(obj);
00438                   pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00439                   if (chan)
00440                      ast_autoservice_stop(chan);
00441                   if (bogus_chan)
00442                      ast_channel_free(chan);
00443                   ast_free(sql);
00444                   return -1;
00445                }
00446                resultset = tmp;
00447                strcpy((char *)resultset + sizeof(*resultset), colnames->str);
00448             }
00449          }
00450 
00451          buflen = strlen(buf);
00452          res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata->str, coldata->len, &indicator);
00453          if (indicator == SQL_NULL_DATA) {
00454             ast_debug(3, "Got NULL data\n");
00455             ast_str_reset(coldata);
00456             res = SQL_SUCCESS;
00457          }
00458 
00459          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00460             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql->str);
00461             y = -1;
00462             buf[0] = '\0';
00463             goto end_acf_read;
00464          }
00465 
00466          ast_debug(2, "Got coldata of '%s'\n", coldata->str);
00467          coldata->used = strlen(coldata->str);
00468 
00469          /* Copy data, encoding '\' and ',' for the argument parser */
00470          for (i = 0; i < coldata->used; i++) {
00471             if (escapecommas && (coldata->str[i] == '\\' || coldata->str[i] == ',')) {
00472                buf[buflen++] = '\\';
00473             }
00474             buf[buflen++] = coldata->str[i];
00475 
00476             if (buflen >= len - 2)
00477                break;
00478 
00479             if (coldata->str[i] == '\0')
00480                break;
00481          }
00482 
00483          buf[buflen++] = ',';
00484          buf[buflen] = '\0';
00485          ast_debug(2, "buf is now set to '%s'\n", buf);
00486       }
00487       /* Trim trailing comma */
00488       buf[buflen - 1] = '\0';
00489       ast_debug(2, "buf is now set to '%s'\n", buf);
00490 
00491       if (resultset) {
00492          row = ast_calloc(1, sizeof(*row) + buflen);
00493          if (!row) {
00494             ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00495             goto end_acf_read;
00496          }
00497          strcpy((char *)row + sizeof(*row), buf);
00498          AST_LIST_INSERT_TAIL(resultset, row, list);
00499 
00500          /* Get next row */
00501          res = SQLFetch(stmt);
00502          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00503             if (res != SQL_NO_DATA)
00504                ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
00505             y++;
00506             break;
00507          }
00508       }
00509    }
00510 
00511 end_acf_read:
00512    snprintf(rowcount, sizeof(rowcount), "%d", y);
00513    pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00514    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames->str);
00515    if (resultset) {
00516       int uid;
00517       struct ast_datastore *odbc_store;
00518       uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00519       snprintf(buf, len, "%d", uid);
00520       odbc_store = ast_datastore_alloc(&odbc_info, buf);
00521       if (!odbc_store) {
00522          ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel.  Results fail.\n");
00523          odbc_datastore_free(resultset);
00524          SQLCloseCursor(stmt);
00525          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00526          ast_odbc_release_obj(obj);
00527          if (chan)
00528             ast_autoservice_stop(chan);
00529          if (bogus_chan)
00530             ast_channel_free(chan);
00531          ast_free(sql);
00532          return -1;
00533       }
00534       odbc_store->data = resultset;
00535       ast_channel_datastore_add(chan, odbc_store);
00536    }
00537    SQLCloseCursor(stmt);
00538    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00539    ast_odbc_release_obj(obj);
00540    if (chan)
00541       ast_autoservice_stop(chan);
00542    if (bogus_chan)
00543       ast_channel_free(chan);
00544    ast_free(sql);
00545    return 0;
00546 }
00547 
00548 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00549 {
00550    char *out = buf;
00551 
00552    for (; *data && out - buf < len; data++) {
00553       if (*data == '\'') {
00554          *out = '\'';
00555          out++;
00556       }
00557       *out++ = *data;
00558    }
00559    *out = '\0';
00560 
00561    return 0;
00562 }
00563 
00564 static struct ast_custom_function escape_function = {
00565    .name = "SQL_ESC",
00566    .synopsis = "Escapes single ticks for use in SQL statements",
00567    .syntax = "SQL_ESC(<string>)",
00568    .desc =
00569 "Used in SQL templates to escape data which may contain single ticks (') which\n"
00570 "are otherwise used to delimit data.  For example:\n"
00571 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
00572    .read = acf_escape,
00573    .write = NULL,
00574 };
00575 
00576 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00577 {
00578    struct ast_datastore *store;
00579    struct odbc_datastore *resultset;
00580    struct odbc_datastore_row *row;
00581    store = ast_channel_datastore_find(chan, &odbc_info, data);
00582    if (!store) {
00583       return -1;
00584    }
00585    resultset = store->data;
00586    AST_LIST_LOCK(resultset);
00587    row = AST_LIST_REMOVE_HEAD(resultset, list);
00588    AST_LIST_UNLOCK(resultset);
00589    if (!row) {
00590       /* Cleanup datastore */
00591       ast_channel_datastore_remove(chan, store);
00592       ast_datastore_free(store);
00593       return -1;
00594    }
00595    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00596    ast_copy_string(buf, row->data, len);
00597    ast_free(row);
00598    return 0;
00599 }
00600 
00601 static struct ast_custom_function fetch_function = {
00602    .name = "ODBC_FETCH",
00603    .synopsis = "Fetch a row from a multirow query",
00604    .syntax = "ODBC_FETCH(<result-id>)",
00605    .desc =
00606 "For queries which are marked as mode=multirow, the original query returns a\n"
00607 "result-id from which results may be fetched.  This function implements the\n"
00608 "actual fetch of the results.\n",
00609    .read = acf_fetch,
00610    .write = NULL,
00611 };
00612 
00613 static char *app_odbcfinish = "ODBCFinish";
00614 static char *syn_odbcfinish = "Clear the resultset of a successful multirow query";
00615 static char *desc_odbcfinish =
00616 "ODBCFinish(<result-id>)\n"
00617 "  Clears any remaining rows of the specified resultset\n";
00618 
00619 
00620 static int exec_odbcfinish(struct ast_channel *chan, void *data)
00621 {
00622    struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00623    if (!store) /* Already freed; no big deal. */
00624       return 0;
00625    ast_channel_datastore_remove(chan, store);
00626    ast_datastore_free(store);
00627    return 0;
00628 }
00629 
00630 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00631 {
00632    const char *tmp;
00633    int i;
00634    int res;
00635 
00636    if (!cfg || !catg) {
00637       return EINVAL;
00638    }
00639 
00640    *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00641    if (! (*query))
00642       return ENOMEM;
00643 
00644    if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00645       char *tmp2 = ast_strdupa(tmp);
00646       AST_DECLARE_APP_ARGS(writeconf,
00647          AST_APP_ARG(dsn)[5];
00648       );
00649       AST_STANDARD_APP_ARGS(writeconf, tmp2);
00650       for (i = 0; i < 5; i++) {
00651          if (!ast_strlen_zero(writeconf.dsn[i]))
00652             ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00653       }
00654    }
00655 
00656    if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00657       char *tmp2 = ast_strdupa(tmp);
00658       AST_DECLARE_APP_ARGS(readconf,
00659          AST_APP_ARG(dsn)[5];
00660       );
00661       AST_STANDARD_APP_ARGS(readconf, tmp2);
00662       for (i = 0; i < 5; i++) {
00663          if (!ast_strlen_zero(readconf.dsn[i]))
00664             ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00665       }
00666    } else {
00667       /* If no separate readhandle, then use the writehandle for reading */
00668       for (i = 0; i < 5; i++) {
00669          if (!ast_strlen_zero((*query)->writehandle[i]))
00670             ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00671       }
00672    }
00673 
00674    if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00675       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00676    else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00677       ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
00678       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00679    }
00680 
00681    if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00682       ast_free(*query);
00683       *query = NULL;
00684       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00685       return EINVAL;
00686    }
00687 
00688    if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00689       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00690    else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00691       ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
00692       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00693    }
00694 
00695    if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00696       ast_free(*query);
00697       *query = NULL;
00698       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00699       return EINVAL;
00700    }
00701 
00702    /* Allow escaping of embedded commas in fields to be turned off */
00703    ast_set_flag((*query), OPT_ESCAPECOMMAS);
00704    if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00705       if (ast_false(tmp))
00706          ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00707    }
00708 
00709    if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00710       if (strcasecmp(tmp, "multirow") == 0)
00711          ast_set_flag((*query), OPT_MULTIROW);
00712       if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00713          sscanf(tmp, "%d", &((*query)->rowlimit));
00714    }
00715 
00716    (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00717    if (! (*query)->acf) {
00718       ast_free(*query);
00719       *query = NULL;
00720       return ENOMEM;
00721    }
00722 
00723    if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00724       if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00725          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00726       }
00727    } else {
00728       if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00729          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00730       }
00731    }
00732 
00733    if (!((*query)->acf->name)) {
00734       ast_free((*query)->acf);
00735       ast_free(*query);
00736       *query = NULL;
00737       return ENOMEM;
00738    }
00739 
00740    if (asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name) < 0) {
00741       ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00742       (*query)->acf->syntax = NULL;
00743    }
00744 
00745    if (!((*query)->acf->syntax)) {
00746       ast_free((char *)(*query)->acf->name);
00747       ast_free((*query)->acf);
00748       ast_free(*query);
00749       *query = NULL;
00750       return ENOMEM;
00751    }
00752 
00753    (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00754 
00755    res = 0;
00756    if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00757       res = asprintf((char **)&((*query)->acf->desc),
00758                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00759                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00760                 "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
00761                 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00762                 "\nRead:\n%s\n\nWrite:\n%s\n",
00763                 (*query)->sql_read,
00764                 (*query)->sql_write);
00765    } else if (!ast_strlen_zero((*query)->sql_read)) {
00766       res = asprintf((char **)&((*query)->acf->desc),
00767                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00768                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00769                 "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
00770                 (*query)->sql_read);
00771    } else if (!ast_strlen_zero((*query)->sql_write)) {
00772       res = asprintf((char **)&((*query)->acf->desc),
00773                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00774                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00775                 "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
00776                 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00777                 "This function may only be set.\nSQL:\n%s\n",
00778                 (*query)->sql_write);
00779    } else {
00780       ast_free((char *)(*query)->acf->syntax);
00781       ast_free((char *)(*query)->acf->name);
00782       ast_free((*query)->acf);
00783       ast_free(*query);
00784       ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute.  Ignoring.\n", catg);
00785       return EINVAL;
00786    }
00787 
00788    if (res < 0) {
00789       ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00790       (*query)->acf->desc = NULL;
00791    }
00792 
00793 
00794    if (!((*query)->acf->desc)) {
00795       ast_free((char *)(*query)->acf->syntax);
00796       ast_free((char *)(*query)->acf->name);
00797       ast_free((*query)->acf);
00798       ast_free(*query);
00799       *query = NULL;
00800       return ENOMEM;
00801    }
00802 
00803    if (ast_strlen_zero((*query)->sql_read)) {
00804       (*query)->acf->read = NULL;
00805    } else {
00806       (*query)->acf->read = acf_odbc_read;
00807    }
00808 
00809    if (ast_strlen_zero((*query)->sql_write)) {
00810       (*query)->acf->write = NULL;
00811    } else {
00812       (*query)->acf->write = acf_odbc_write;
00813    }
00814 
00815    return 0;
00816 }
00817 
00818 static int free_acf_query(struct acf_odbc_query *query)
00819 {
00820    if (query) {
00821       if (query->acf) {
00822          if (query->acf->name)
00823             ast_free((char *)query->acf->name);
00824          if (query->acf->syntax)
00825             ast_free((char *)query->acf->syntax);
00826          if (query->acf->desc)
00827             ast_free((char *)query->acf->desc);
00828          ast_free(query->acf);
00829       }
00830       ast_free(query);
00831    }
00832    return 0;
00833 }
00834 
00835 static int load_module(void)
00836 {
00837    int res = 0;
00838    struct ast_config *cfg;
00839    char *catg;
00840    struct ast_flags config_flags = { 0 };
00841 
00842    res |= ast_custom_function_register(&fetch_function);
00843    res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
00844    AST_RWLIST_WRLOCK(&queries);
00845 
00846    cfg = ast_config_load(config, config_flags);
00847    if (!cfg) {
00848       ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
00849       AST_RWLIST_UNLOCK(&queries);
00850       return AST_MODULE_LOAD_DECLINE;
00851    }
00852 
00853    for (catg = ast_category_browse(cfg, NULL);
00854         catg;
00855         catg = ast_category_browse(cfg, catg)) {
00856       struct acf_odbc_query *query = NULL;
00857       int err;
00858 
00859       if ((err = init_acf_query(cfg, catg, &query))) {
00860          if (err == ENOMEM)
00861             ast_log(LOG_ERROR, "Out of memory\n");
00862          else if (err == EINVAL)
00863             ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
00864          else
00865             ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
00866       } else {
00867          AST_RWLIST_INSERT_HEAD(&queries, query, list);
00868          ast_custom_function_register(query->acf);
00869       }
00870    }
00871 
00872    ast_config_destroy(cfg);
00873    res |= ast_custom_function_register(&escape_function);
00874 
00875    AST_RWLIST_UNLOCK(&queries);
00876    return res;
00877 }
00878 
00879 static int unload_module(void)
00880 {
00881    struct acf_odbc_query *query;
00882    int res = 0;
00883 
00884    AST_RWLIST_WRLOCK(&queries);
00885    while (!AST_RWLIST_EMPTY(&queries)) {
00886       query = AST_RWLIST_REMOVE_HEAD(&queries, list);
00887       ast_custom_function_unregister(query->acf);
00888       free_acf_query(query);
00889    }
00890 
00891    res |= ast_custom_function_unregister(&escape_function);
00892    res |= ast_custom_function_unregister(&fetch_function);
00893    res |= ast_unregister_application(app_odbcfinish);
00894 
00895    /* Allow any threads waiting for this lock to pass (avoids a race) */
00896    AST_RWLIST_UNLOCK(&queries);
00897    usleep(1);
00898    AST_RWLIST_WRLOCK(&queries);
00899 
00900    AST_RWLIST_UNLOCK(&queries);
00901    return 0;
00902 }
00903 
00904 static int reload(void)
00905 {
00906    int res = 0;
00907    struct ast_config *cfg;
00908    struct acf_odbc_query *oldquery;
00909    char *catg;
00910    struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
00911 
00912    cfg = ast_config_load(config, config_flags);
00913    if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00914       return 0;
00915 
00916    AST_RWLIST_WRLOCK(&queries);
00917 
00918    while (!AST_RWLIST_EMPTY(&queries)) {
00919       oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
00920       ast_custom_function_unregister(oldquery->acf);
00921       free_acf_query(oldquery);
00922    }
00923 
00924    if (!cfg) {
00925       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00926       goto reload_out;
00927    }
00928 
00929    for (catg = ast_category_browse(cfg, NULL);
00930         catg;
00931         catg = ast_category_browse(cfg, catg)) {
00932       struct acf_odbc_query *query = NULL;
00933 
00934       if (init_acf_query(cfg, catg, &query)) {
00935          ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00936       } else {
00937          AST_RWLIST_INSERT_HEAD(&queries, query, list);
00938          ast_custom_function_register(query->acf);
00939       }
00940    }
00941 
00942    ast_config_destroy(cfg);
00943 reload_out:
00944    AST_RWLIST_UNLOCK(&queries);
00945    return res;
00946 }
00947 
00948 /* XXX need to revise usecount - set if query_lock is set */
00949 
00950 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
00951       .load = load_module,
00952       .unload = unload_module,
00953       .reload = reload,
00954           );
00955 

Generated on Fri Jul 24 00:40:56 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7