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

Generated on Wed Oct 28 11:45:38 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6