Wed Mar 3 22:36:51 2010

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: 211569 $")
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       buf[0] = '\0';
00395       for (x = 0; x < colcount; x++) {
00396          int i;
00397          struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00398 
00399          if (y == 0) {
00400             char colname[256];
00401             SQLULEN maxcol;
00402 
00403             res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00404             ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00405             if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00406                snprintf(colname, sizeof(colname), "field%d", x);
00407             }
00408 
00409             if (coldata->len < maxcol + 1) {
00410                ast_str_make_space(&coldata, maxcol + 1);
00411             }
00412 
00413             if (colnames->used) {
00414                ast_str_append(&colnames, 0, ",");
00415             }
00416             ast_str_make_space(&colnames, strlen(colname) * 2 + 1 + colnames->used);
00417 
00418             /* Copy data, encoding '\' and ',' for the argument parser */
00419             for (i = 0; i < sizeof(colname); i++) {
00420                if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
00421                   colnames->str[colnames->used++] = '\\';
00422                }
00423                colnames->str[colnames->used++] = colname[i];
00424 
00425                if (colname[i] == '\0') {
00426                   colnames->used--;
00427                   break;
00428                }
00429             }
00430 
00431             if (resultset) {
00432                void *tmp = ast_realloc(resultset, sizeof(*resultset) + colnames->used + 1);
00433                if (!tmp) {
00434                   ast_log(LOG_ERROR, "No space for a new resultset?\n");
00435                   ast_free(resultset);
00436                   SQLCloseCursor(stmt);
00437                   SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00438                   ast_odbc_release_obj(obj);
00439                   pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00440                   if (chan)
00441                      ast_autoservice_stop(chan);
00442                   if (bogus_chan)
00443                      ast_channel_free(chan);
00444                   ast_free(sql);
00445                   return -1;
00446                }
00447                resultset = tmp;
00448                strcpy((char *)resultset + sizeof(*resultset), colnames->str);
00449             }
00450          }
00451 
00452          buflen = strlen(buf);
00453          res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata->str, coldata->len, &indicator);
00454          if (indicator == SQL_NULL_DATA) {
00455             ast_debug(3, "Got NULL data\n");
00456             ast_str_reset(coldata);
00457             res = SQL_SUCCESS;
00458          }
00459 
00460          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00461             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql->str);
00462             y = -1;
00463             buf[0] = '\0';
00464             goto end_acf_read;
00465          }
00466 
00467          ast_debug(2, "Got coldata of '%s'\n", coldata->str);
00468          coldata->used = strlen(coldata->str);
00469 
00470          /* Copy data, encoding '\' and ',' for the argument parser */
00471          for (i = 0; i < coldata->used; i++) {
00472             if (escapecommas && (coldata->str[i] == '\\' || coldata->str[i] == ',')) {
00473                buf[buflen++] = '\\';
00474             }
00475             buf[buflen++] = coldata->str[i];
00476 
00477             if (buflen >= len - 2)
00478                break;
00479 
00480             if (coldata->str[i] == '\0')
00481                break;
00482          }
00483 
00484          buf[buflen++] = ',';
00485          buf[buflen] = '\0';
00486          ast_debug(2, "buf is now set to '%s'\n", buf);
00487       }
00488       /* Trim trailing comma */
00489       buf[buflen - 1] = '\0';
00490       ast_debug(2, "buf is now set to '%s'\n", buf);
00491 
00492       if (resultset) {
00493          row = ast_calloc(1, sizeof(*row) + buflen);
00494          if (!row) {
00495             ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00496             goto end_acf_read;
00497          }
00498          strcpy((char *)row + sizeof(*row), buf);
00499          AST_LIST_INSERT_TAIL(resultset, row, list);
00500 
00501          /* Get next row */
00502          res = SQLFetch(stmt);
00503          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00504             if (res != SQL_NO_DATA)
00505                ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
00506             y++;
00507             break;
00508          }
00509       }
00510    }
00511 
00512 end_acf_read:
00513    snprintf(rowcount, sizeof(rowcount), "%d", y);
00514    pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00515    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames->str);
00516    if (resultset) {
00517       int uid;
00518       struct ast_datastore *odbc_store;
00519       uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00520       snprintf(buf, len, "%d", uid);
00521       odbc_store = ast_datastore_alloc(&odbc_info, buf);
00522       if (!odbc_store) {
00523          ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel.  Results fail.\n");
00524          odbc_datastore_free(resultset);
00525          SQLCloseCursor(stmt);
00526          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00527          ast_odbc_release_obj(obj);
00528          if (chan)
00529             ast_autoservice_stop(chan);
00530          if (bogus_chan)
00531             ast_channel_free(chan);
00532          ast_free(sql);
00533          return -1;
00534       }
00535       odbc_store->data = resultset;
00536       ast_channel_datastore_add(chan, odbc_store);
00537    }
00538    SQLCloseCursor(stmt);
00539    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00540    ast_odbc_release_obj(obj);
00541    if (chan)
00542       ast_autoservice_stop(chan);
00543    if (bogus_chan)
00544       ast_channel_free(chan);
00545    ast_free(sql);
00546    return 0;
00547 }
00548 
00549 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00550 {
00551    char *out = buf;
00552 
00553    for (; *data && out - buf < len; data++) {
00554       if (*data == '\'') {
00555          *out = '\'';
00556          out++;
00557       }
00558       *out++ = *data;
00559    }
00560    *out = '\0';
00561 
00562    return 0;
00563 }
00564 
00565 static struct ast_custom_function escape_function = {
00566    .name = "SQL_ESC",
00567    .synopsis = "Escapes single ticks for use in SQL statements",
00568    .syntax = "SQL_ESC(<string>)",
00569    .desc =
00570 "Used in SQL templates to escape data which may contain single ticks (') which\n"
00571 "are otherwise used to delimit data.  For example:\n"
00572 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
00573    .read = acf_escape,
00574    .write = NULL,
00575 };
00576 
00577 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00578 {
00579    struct ast_datastore *store;
00580    struct odbc_datastore *resultset;
00581    struct odbc_datastore_row *row;
00582    store = ast_channel_datastore_find(chan, &odbc_info, data);
00583    if (!store) {
00584       return -1;
00585    }
00586    resultset = store->data;
00587    AST_LIST_LOCK(resultset);
00588    row = AST_LIST_REMOVE_HEAD(resultset, list);
00589    AST_LIST_UNLOCK(resultset);
00590    if (!row) {
00591       /* Cleanup datastore */
00592       ast_channel_datastore_remove(chan, store);
00593       ast_datastore_free(store);
00594       return -1;
00595    }
00596    pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00597    ast_copy_string(buf, row->data, len);
00598    ast_free(row);
00599    return 0;
00600 }
00601 
00602 static struct ast_custom_function fetch_function = {
00603    .name = "ODBC_FETCH",
00604    .synopsis = "Fetch a row from a multirow query",
00605    .syntax = "ODBC_FETCH(<result-id>)",
00606    .desc =
00607 "For queries which are marked as mode=multirow, the original query returns a\n"
00608 "result-id from which results may be fetched.  This function implements the\n"
00609 "actual fetch of the results.\n",
00610    .read = acf_fetch,
00611    .write = NULL,
00612 };
00613 
00614 static char *app_odbcfinish = "ODBCFinish";
00615 static char *syn_odbcfinish = "Clear the resultset of a successful multirow query";
00616 static char *desc_odbcfinish =
00617 "ODBCFinish(<result-id>)\n"
00618 "  Clears any remaining rows of the specified resultset\n";
00619 
00620 
00621 static int exec_odbcfinish(struct ast_channel *chan, void *data)
00622 {
00623    struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00624    if (!store) /* Already freed; no big deal. */
00625       return 0;
00626    ast_channel_datastore_remove(chan, store);
00627    ast_datastore_free(store);
00628    return 0;
00629 }
00630 
00631 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00632 {
00633    const char *tmp;
00634    int i;
00635    int res;
00636 
00637    if (!cfg || !catg) {
00638       return EINVAL;
00639    }
00640 
00641    *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00642    if (! (*query))
00643       return ENOMEM;
00644 
00645    if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00646       char *tmp2 = ast_strdupa(tmp);
00647       AST_DECLARE_APP_ARGS(writeconf,
00648          AST_APP_ARG(dsn)[5];
00649       );
00650       AST_STANDARD_APP_ARGS(writeconf, tmp2);
00651       for (i = 0; i < 5; i++) {
00652          if (!ast_strlen_zero(writeconf.dsn[i]))
00653             ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00654       }
00655    }
00656 
00657    if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00658       char *tmp2 = ast_strdupa(tmp);
00659       AST_DECLARE_APP_ARGS(readconf,
00660          AST_APP_ARG(dsn)[5];
00661       );
00662       AST_STANDARD_APP_ARGS(readconf, tmp2);
00663       for (i = 0; i < 5; i++) {
00664          if (!ast_strlen_zero(readconf.dsn[i]))
00665             ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00666       }
00667    } else {
00668       /* If no separate readhandle, then use the writehandle for reading */
00669       for (i = 0; i < 5; i++) {
00670          if (!ast_strlen_zero((*query)->writehandle[i]))
00671             ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00672       }
00673    }
00674 
00675    if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00676       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00677    else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00678       ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg);
00679       ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00680    }
00681 
00682    if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00683       ast_free(*query);
00684       *query = NULL;
00685       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00686       return EINVAL;
00687    }
00688 
00689    if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00690       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00691    else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00692       ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg);
00693       ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00694    }
00695 
00696    if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00697       ast_free(*query);
00698       *query = NULL;
00699       ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00700       return EINVAL;
00701    }
00702 
00703    /* Allow escaping of embedded commas in fields to be turned off */
00704    ast_set_flag((*query), OPT_ESCAPECOMMAS);
00705    if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00706       if (ast_false(tmp))
00707          ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00708    }
00709 
00710    if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00711       if (strcasecmp(tmp, "multirow") == 0)
00712          ast_set_flag((*query), OPT_MULTIROW);
00713       if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00714          sscanf(tmp, "%30d", &((*query)->rowlimit));
00715    }
00716 
00717    (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00718    if (! (*query)->acf) {
00719       ast_free(*query);
00720       *query = NULL;
00721       return ENOMEM;
00722    }
00723 
00724    if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00725       if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00726          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00727       }
00728    } else {
00729       if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00730          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00731       }
00732    }
00733 
00734    if (!((*query)->acf->name)) {
00735       ast_free((*query)->acf);
00736       ast_free(*query);
00737       *query = NULL;
00738       return ENOMEM;
00739    }
00740 
00741    if (asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name) < 0) {
00742       ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00743       (*query)->acf->syntax = NULL;
00744    }
00745 
00746    if (!((*query)->acf->syntax)) {
00747       ast_free((char *)(*query)->acf->name);
00748       ast_free((*query)->acf);
00749       ast_free(*query);
00750       *query = NULL;
00751       return ENOMEM;
00752    }
00753 
00754    (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00755 
00756    res = 0;
00757    if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00758       res = asprintf((char **)&((*query)->acf->desc),
00759                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00760                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00761                 "${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
00762                 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00763                 "\nRead:\n%s\n\nWrite:\n%s\n",
00764                 (*query)->sql_read,
00765                 (*query)->sql_write);
00766    } else if (!ast_strlen_zero((*query)->sql_read)) {
00767       res = asprintf((char **)&((*query)->acf->desc),
00768                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00769                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00770                 "${ARG2}, ... ${ARGn}.  This function may only be read, not set.\n\nSQL:\n%s\n",
00771                 (*query)->sql_read);
00772    } else if (!ast_strlen_zero((*query)->sql_write)) {
00773       res = asprintf((char **)&((*query)->acf->desc),
00774                 "Runs the following query, as defined in func_odbc.conf, performing\n"
00775                 "substitution of the arguments into the query as specified by ${ARG1},\n"
00776                 "${ARG2}, ... ${ARGn}.  The values are provided either in whole as\n"
00777                 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00778                 "This function may only be set.\nSQL:\n%s\n",
00779                 (*query)->sql_write);
00780    } else {
00781       ast_free((char *)(*query)->acf->syntax);
00782       ast_free((char *)(*query)->acf->name);
00783       ast_free((*query)->acf);
00784       ast_free(*query);
00785       ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute.  Ignoring.\n", catg);
00786       return EINVAL;
00787    }
00788 
00789    if (res < 0) {
00790       ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00791       (*query)->acf->desc = NULL;
00792    }
00793 
00794 
00795    if (!((*query)->acf->desc)) {
00796       ast_free((char *)(*query)->acf->syntax);
00797       ast_free((char *)(*query)->acf->name);
00798       ast_free((*query)->acf);
00799       ast_free(*query);
00800       *query = NULL;
00801       return ENOMEM;
00802    }
00803 
00804    if (ast_strlen_zero((*query)->sql_read)) {
00805       (*query)->acf->read = NULL;
00806    } else {
00807       (*query)->acf->read = acf_odbc_read;
00808    }
00809 
00810    if (ast_strlen_zero((*query)->sql_write)) {
00811       (*query)->acf->write = NULL;
00812    } else {
00813       (*query)->acf->write = acf_odbc_write;
00814    }
00815 
00816    return 0;
00817 }
00818 
00819 static int free_acf_query(struct acf_odbc_query *query)
00820 {
00821    if (query) {
00822       if (query->acf) {
00823          if (query->acf->name)
00824             ast_free((char *)query->acf->name);
00825          if (query->acf->syntax)
00826             ast_free((char *)query->acf->syntax);
00827          if (query->acf->desc)
00828             ast_free((char *)query->acf->desc);
00829          ast_free(query->acf);
00830       }
00831       ast_free(query);
00832    }
00833    return 0;
00834 }
00835 
00836 static int load_module(void)
00837 {
00838    int res = 0;
00839    struct ast_config *cfg;
00840    char *catg;
00841    struct ast_flags config_flags = { 0 };
00842 
00843    res |= ast_custom_function_register(&fetch_function);
00844    res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
00845    AST_RWLIST_WRLOCK(&queries);
00846 
00847    cfg = ast_config_load(config, config_flags);
00848    if (!cfg) {
00849       ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
00850       AST_RWLIST_UNLOCK(&queries);
00851       return AST_MODULE_LOAD_DECLINE;
00852    }
00853 
00854    for (catg = ast_category_browse(cfg, NULL);
00855         catg;
00856         catg = ast_category_browse(cfg, catg)) {
00857       struct acf_odbc_query *query = NULL;
00858       int err;
00859 
00860       if ((err = init_acf_query(cfg, catg, &query))) {
00861          if (err == ENOMEM)
00862             ast_log(LOG_ERROR, "Out of memory\n");
00863          else if (err == EINVAL)
00864             ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
00865          else
00866             ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
00867       } else {
00868          AST_RWLIST_INSERT_HEAD(&queries, query, list);
00869          ast_custom_function_register(query->acf);
00870       }
00871    }
00872 
00873    ast_config_destroy(cfg);
00874    res |= ast_custom_function_register(&escape_function);
00875 
00876    AST_RWLIST_UNLOCK(&queries);
00877    return res;
00878 }
00879 
00880 static int unload_module(void)
00881 {
00882    struct acf_odbc_query *query;
00883    int res = 0;
00884 
00885    AST_RWLIST_WRLOCK(&queries);
00886    while (!AST_RWLIST_EMPTY(&queries)) {
00887       query = AST_RWLIST_REMOVE_HEAD(&queries, list);
00888       ast_custom_function_unregister(query->acf);
00889       free_acf_query(query);
00890    }
00891 
00892    res |= ast_custom_function_unregister(&escape_function);
00893    res |= ast_custom_function_unregister(&fetch_function);
00894    res |= ast_unregister_application(app_odbcfinish);
00895 
00896    /* Allow any threads waiting for this lock to pass (avoids a race) */
00897    AST_RWLIST_UNLOCK(&queries);
00898    usleep(1);
00899    AST_RWLIST_WRLOCK(&queries);
00900 
00901    AST_RWLIST_UNLOCK(&queries);
00902    return 0;
00903 }
00904 
00905 static int reload(void)
00906 {
00907    int res = 0;
00908    struct ast_config *cfg;
00909    struct acf_odbc_query *oldquery;
00910    char *catg;
00911    struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
00912 
00913    cfg = ast_config_load(config, config_flags);
00914    if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00915       return 0;
00916 
00917    AST_RWLIST_WRLOCK(&queries);
00918 
00919    while (!AST_RWLIST_EMPTY(&queries)) {
00920       oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
00921       ast_custom_function_unregister(oldquery->acf);
00922       free_acf_query(oldquery);
00923    }
00924 
00925    if (!cfg) {
00926       ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00927       goto reload_out;
00928    }
00929 
00930    for (catg = ast_category_browse(cfg, NULL);
00931         catg;
00932         catg = ast_category_browse(cfg, catg)) {
00933       struct acf_odbc_query *query = NULL;
00934 
00935       if (init_acf_query(cfg, catg, &query)) {
00936          ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00937       } else {
00938          AST_RWLIST_INSERT_HEAD(&queries, query, list);
00939          ast_custom_function_register(query->acf);
00940       }
00941    }
00942 
00943    ast_config_destroy(cfg);
00944 reload_out:
00945    AST_RWLIST_UNLOCK(&queries);
00946    return res;
00947 }
00948 
00949 /* XXX need to revise usecount - set if query_lock is set */
00950 
00951 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
00952       .load = load_module,
00953       .unload = unload_module,
00954       .reload = reload,
00955           );
00956 

Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1