Tue Aug 24 2010 19:41:32

Asterisk developer's documentation


res_config_odbc.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2010, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
00009  *
00010  * See http://www.asterisk.org for more information about
00011  * the Asterisk project. Please do not directly contact
00012  * any of the maintainers of this project for assistance;
00013  * the project provides a web site, mailing lists and IRC
00014  * channels for your use.
00015  *
00016  * This program is free software, distributed under the terms of
00017  * the GNU General Public License Version 2. See the LICENSE file
00018  * at the top of the source tree.
00019  */
00020 
00021 /*! \file
00022  *
00023  * \brief odbc+odbc plugin for portable configuration engine
00024  *
00025  * \author Mark Spencer <markster@digium.com>
00026  * \author Anthony Minessale II <anthmct@yahoo.com>
00027  *
00028  * \arg http://www.unixodbc.org
00029  */
00030 
00031 /*** MODULEINFO
00032    <depend>res_odbc</depend>
00033  ***/
00034 
00035 #include "asterisk.h"
00036 
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 277774 $")
00038 
00039 #include "asterisk/file.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/res_odbc.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/stringfields.h"
00048 
00049 AST_THREADSTORAGE(sql_buf);
00050 
00051 struct custom_prepare_struct {
00052    const char *sql;
00053    const char *extra;
00054    AST_DECLARE_STRING_FIELDS(
00055       AST_STRING_FIELD(encoding)[256];
00056    );
00057    va_list ap;
00058    unsigned long long skip;
00059 };
00060 
00061 static void decode_chunk(char *chunk)
00062 {
00063    for (; *chunk; chunk++) {
00064       if (*chunk == '^' && strchr("0123456789ABCDEFabcdef", chunk[1]) && strchr("0123456789ABCDEFabcdef", chunk[2])) {
00065          sscanf(chunk + 1, "%02hhd", chunk);
00066          memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
00067       }
00068    }
00069 }
00070 
00071 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
00072 {
00073    int res, x = 1, count = 0;
00074    struct custom_prepare_struct *cps = data;
00075    const char *newparam, *newval;
00076    char encodebuf[1024];
00077    SQLHSTMT stmt;
00078    va_list ap;
00079 
00080    va_copy(ap, cps->ap);
00081 
00082    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00083    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00084       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00085       return NULL;
00086    }
00087 
00088    ast_debug(1, "Skip: %lld; SQL: %s\n", cps->skip, cps->sql);
00089 
00090    res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
00091    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00092       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
00093       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00094       return NULL;
00095    }
00096 
00097    while ((newparam = va_arg(ap, const char *))) {
00098       newval = va_arg(ap, const char *);
00099       if ((1LL << count++) & cps->skip) {
00100          ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", newparam, newval, 1LL << (count - 1), cps->skip);
00101          continue;
00102       }
00103       ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, newparam, newval);
00104       if (strchr(newval, ';') || strchr(newval, '^')) {
00105          char *eptr = encodebuf;
00106          const char *vptr = newval;
00107          for (; *vptr && eptr < encodebuf + sizeof(encodebuf); vptr++) {
00108             if (strchr("^;", *vptr)) {
00109                /* We use ^XX, instead of %XX because '%' is a special character in SQL */
00110                snprintf(eptr, encodebuf + sizeof(encodebuf) - eptr, "^%02hhX", *vptr);
00111                eptr += 3;
00112                vptr++;
00113             } else {
00114                *eptr++ = *vptr++;
00115             }
00116          }
00117          if (eptr < encodebuf + sizeof(encodebuf)) {
00118             *eptr = '\0';
00119          } else {
00120             encodebuf[sizeof(encodebuf) - 1] = '\0';
00121          }
00122          ast_string_field_set(cps, encoding[x], encodebuf);
00123          newval = cps->encoding[x];
00124       }
00125       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00126    }
00127    va_end(ap);
00128 
00129    if (!ast_strlen_zero(cps->extra))
00130       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
00131    return stmt;
00132 }
00133 
00134 /*!
00135  * \brief Excute an SQL query and return ast_variable list
00136  * \param database
00137  * \param table
00138  * \param ap list containing one or more field/operator/value set.
00139  *
00140  * Select database and preform query on table, prepare the sql statement
00141  * Sub-in the values to the prepared statement and execute it. Return results
00142  * as a ast_variable list.
00143  *
00144  * \retval var on success
00145  * \retval NULL on failure
00146 */
00147 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
00148 {
00149    struct odbc_obj *obj;
00150    SQLHSTMT stmt;
00151    char sql[1024];
00152    char coltitle[256];
00153    char rowdata[2048];
00154    char *op;
00155    const char *newparam, *newval;
00156    char *stringp;
00157    char *chunk;
00158    SQLSMALLINT collen;
00159    int res;
00160    int x;
00161    struct ast_variable *var=NULL, *prev=NULL;
00162    SQLULEN colsize;
00163    SQLSMALLINT colcount=0;
00164    SQLSMALLINT datatype;
00165    SQLSMALLINT decimaldigits;
00166    SQLSMALLINT nullable;
00167    SQLLEN indicator;
00168    va_list aq;
00169    struct custom_prepare_struct cps = { .sql = sql };
00170 
00171    if (ast_string_field_init(&cps, 256)) {
00172       return NULL;
00173    }
00174    va_copy(cps.ap, ap);
00175    va_copy(aq, ap);
00176 
00177    if (!table) {
00178       ast_string_field_free_memory(&cps);
00179       return NULL;
00180    }
00181 
00182    obj = ast_odbc_request_obj(database, 0);
00183 
00184    if (!obj) {
00185       ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
00186       ast_string_field_free_memory(&cps);
00187       return NULL;
00188    }
00189 
00190    newparam = va_arg(aq, const char *);
00191    if (!newparam) {
00192       ast_odbc_release_obj(obj);
00193       ast_string_field_free_memory(&cps);
00194       return NULL;
00195    }
00196    newval = va_arg(aq, const char *);
00197    op = !strchr(newparam, ' ') ? " =" : "";
00198    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00199       strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00200    while((newparam = va_arg(aq, const char *))) {
00201       op = !strchr(newparam, ' ') ? " =" : "";
00202       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00203          strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00204       newval = va_arg(aq, const char *);
00205    }
00206    va_end(aq);
00207 
00208    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00209 
00210    if (!stmt) {
00211       ast_odbc_release_obj(obj);
00212       ast_string_field_free_memory(&cps);
00213       return NULL;
00214    }
00215 
00216    res = SQLNumResultCols(stmt, &colcount);
00217    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00218       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00219       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00220       ast_odbc_release_obj(obj);
00221       ast_string_field_free_memory(&cps);
00222       return NULL;
00223    }
00224 
00225    res = SQLFetch(stmt);
00226    if (res == SQL_NO_DATA) {
00227       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00228       ast_odbc_release_obj(obj);
00229       ast_string_field_free_memory(&cps);
00230       return NULL;
00231    }
00232    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00233       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00234       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00235       ast_odbc_release_obj(obj);
00236       ast_string_field_free_memory(&cps);
00237       return NULL;
00238    }
00239    for (x = 0; x < colcount; x++) {
00240       rowdata[0] = '\0';
00241       collen = sizeof(coltitle);
00242       res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
00243                &datatype, &colsize, &decimaldigits, &nullable);
00244       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00245          ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00246          if (var)
00247             ast_variables_destroy(var);
00248          ast_odbc_release_obj(obj);
00249          ast_string_field_free_memory(&cps);
00250          return NULL;
00251       }
00252 
00253       indicator = 0;
00254       res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
00255       if (indicator == SQL_NULL_DATA)
00256          rowdata[0] = '\0';
00257       else if (ast_strlen_zero(rowdata)) {
00258          /* Because we encode the empty string for a NULL, we will encode
00259           * actual empty strings as a string containing a single whitespace. */
00260          ast_copy_string(rowdata, " ", sizeof(rowdata));
00261       }
00262 
00263       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00264          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00265          if (var)
00266             ast_variables_destroy(var);
00267          ast_odbc_release_obj(obj);
00268          return NULL;
00269       }
00270       stringp = rowdata;
00271       while (stringp) {
00272          chunk = strsep(&stringp, ";");
00273          if (!ast_strlen_zero(ast_strip(chunk))) {
00274             if (strchr(chunk, '^')) {
00275                decode_chunk(chunk);
00276             }
00277             if (prev) {
00278                prev->next = ast_variable_new(coltitle, chunk, "");
00279                if (prev->next) {
00280                   prev = prev->next;
00281                }
00282             } else {
00283                prev = var = ast_variable_new(coltitle, chunk, "");
00284             }
00285          }
00286       }
00287    }
00288 
00289 
00290    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00291    ast_odbc_release_obj(obj);
00292    ast_string_field_free_memory(&cps);
00293    return var;
00294 }
00295 
00296 /*!
00297  * \brief Excute an Select query and return ast_config list
00298  * \param database
00299  * \param table
00300  * \param ap list containing one or more field/operator/value set.
00301  *
00302  * Select database and preform query on table, prepare the sql statement
00303  * Sub-in the values to the prepared statement and execute it. 
00304  * Execute this prepared query against several ODBC connected databases.
00305  * Return results as an ast_config variable.
00306  *
00307  * \retval var on success
00308  * \retval NULL on failure
00309 */
00310 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
00311 {
00312    struct odbc_obj *obj;
00313    SQLHSTMT stmt;
00314    char sql[1024];
00315    char coltitle[256];
00316    char rowdata[2048];
00317    const char *initfield=NULL;
00318    char *op;
00319    const char *newparam, *newval;
00320    char *stringp;
00321    char *chunk;
00322    SQLSMALLINT collen;
00323    int res;
00324    int x;
00325    struct ast_variable *var=NULL;
00326    struct ast_config *cfg=NULL;
00327    struct ast_category *cat=NULL;
00328    SQLULEN colsize;
00329    SQLSMALLINT colcount=0;
00330    SQLSMALLINT datatype;
00331    SQLSMALLINT decimaldigits;
00332    SQLSMALLINT nullable;
00333    SQLLEN indicator;
00334    struct custom_prepare_struct cps = { .sql = sql };
00335    va_list aq;
00336 
00337    if (!table || ast_string_field_init(&cps, 256)) {
00338       return NULL;
00339    }
00340    va_copy(cps.ap, ap);
00341    va_copy(aq, ap);
00342 
00343 
00344    obj = ast_odbc_request_obj(database, 0);
00345    if (!obj) {
00346       ast_string_field_free_memory(&cps);
00347       return NULL;
00348    }
00349 
00350    newparam = va_arg(aq, const char *);
00351    if (!newparam)  {
00352       ast_odbc_release_obj(obj);
00353       ast_string_field_free_memory(&cps);
00354       return NULL;
00355    }
00356    initfield = ast_strdupa(newparam);
00357    if ((op = strchr(initfield, ' '))) 
00358       *op = '\0';
00359    newval = va_arg(aq, const char *);
00360    op = !strchr(newparam, ' ') ? " =" : "";
00361    snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00362       strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00363    while((newparam = va_arg(aq, const char *))) {
00364       op = !strchr(newparam, ' ') ? " =" : "";
00365       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00366          strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00367       newval = va_arg(aq, const char *);
00368    }
00369    if (initfield)
00370       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
00371    va_end(aq);
00372 
00373    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00374 
00375    if (!stmt) {
00376       ast_odbc_release_obj(obj);
00377       ast_string_field_free_memory(&cps);
00378       return NULL;
00379    }
00380 
00381    res = SQLNumResultCols(stmt, &colcount);
00382    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00383       ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00384       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00385       ast_odbc_release_obj(obj);
00386       ast_string_field_free_memory(&cps);
00387       return NULL;
00388    }
00389 
00390    cfg = ast_config_new();
00391    if (!cfg) {
00392       ast_log(LOG_WARNING, "Out of memory!\n");
00393       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00394       ast_odbc_release_obj(obj);
00395       ast_string_field_free_memory(&cps);
00396       return NULL;
00397    }
00398 
00399    while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
00400       var = NULL;
00401       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00402          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00403          continue;
00404       }
00405       cat = ast_category_new("","",99999);
00406       if (!cat) {
00407          ast_log(LOG_WARNING, "Out of memory!\n");
00408          continue;
00409       }
00410       for (x=0;x<colcount;x++) {
00411          rowdata[0] = '\0';
00412          collen = sizeof(coltitle);
00413          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
00414                   &datatype, &colsize, &decimaldigits, &nullable);
00415          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00416             ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00417             ast_category_destroy(cat);
00418             continue;
00419          }
00420 
00421          indicator = 0;
00422          res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
00423          if (indicator == SQL_NULL_DATA)
00424             continue;
00425 
00426          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00427             ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00428             ast_category_destroy(cat);
00429             continue;
00430          }
00431          stringp = rowdata;
00432          while (stringp) {
00433             chunk = strsep(&stringp, ";");
00434             if (!ast_strlen_zero(ast_strip(chunk))) {
00435                if (strchr(chunk, '^')) {
00436                   decode_chunk(chunk);
00437                }
00438                if (initfield && !strcmp(initfield, coltitle)) {
00439                   ast_category_rename(cat, chunk);
00440                }
00441                var = ast_variable_new(coltitle, chunk, "");
00442                ast_variable_append(cat, var);
00443             }
00444          }
00445       }
00446       ast_category_append(cfg, cat);
00447    }
00448 
00449    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00450    ast_odbc_release_obj(obj);
00451    ast_string_field_free_memory(&cps);
00452    return cfg;
00453 }
00454 
00455 /*!
00456  * \brief Excute an UPDATE query
00457  * \param database
00458  * \param table
00459  * \param keyfield where clause field
00460  * \param lookup value of field for where clause
00461  * \param ap list containing one or more field/value set(s).
00462  *
00463  * Update a database table, prepare the sql statement using keyfield and lookup
00464  * control the number of records to change. All values to be changed are stored in ap list.
00465  * Sub-in the values to the prepared statement and execute it.
00466  *
00467  * \retval number of rows affected
00468  * \retval -1 on failure
00469 */
00470 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00471 {
00472    struct odbc_obj *obj;
00473    SQLHSTMT stmt;
00474    char sql[256];
00475    SQLLEN rowcount=0;
00476    const char *newparam, *newval;
00477    int res, count = 1;
00478    va_list aq;
00479    struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00480    struct odbc_cache_tables *tableptr;
00481    struct odbc_cache_columns *column;
00482 
00483    if (!table) {
00484       return -1;
00485    }
00486 
00487    va_copy(cps.ap, ap);
00488    va_copy(aq, ap);
00489 
00490    if (ast_string_field_init(&cps, 256)) {
00491       return -1;
00492    }
00493 
00494    tableptr = ast_odbc_find_table(database, table);
00495    if (!(obj = ast_odbc_request_obj(database, 0))) {
00496       ast_odbc_release_table(tableptr);
00497       ast_string_field_free_memory(&cps);
00498       return -1;
00499    }
00500 
00501    newparam = va_arg(aq, const char *);
00502    if (!newparam)  {
00503       ast_odbc_release_obj(obj);
00504       ast_odbc_release_table(tableptr);
00505       ast_string_field_free_memory(&cps);
00506       return -1;
00507    }
00508    newval = va_arg(aq, const char *);
00509 
00510    if (tableptr && !(column = ast_odbc_find_column(tableptr, newparam))) {
00511       ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", newparam, table, database);
00512    }
00513 
00514    snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
00515    while((newparam = va_arg(aq, const char *))) {
00516       newval = va_arg(aq, const char *);
00517       if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count > 63) {
00518          snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
00519       } else { /* the column does not exist in the table */
00520          cps.skip |= (1LL << count);
00521       }
00522       count++;
00523    }
00524    va_end(aq);
00525    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
00526    ast_odbc_release_table(tableptr);
00527 
00528    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00529 
00530    if (!stmt) {
00531       ast_odbc_release_obj(obj);
00532       ast_string_field_free_memory(&cps);
00533       return -1;
00534    }
00535 
00536    res = SQLRowCount(stmt, &rowcount);
00537    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00538    ast_odbc_release_obj(obj);
00539    ast_string_field_free_memory(&cps);
00540 
00541    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00542       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00543       return -1;
00544    }
00545 
00546    if (rowcount >= 0) {
00547       return (int) rowcount;
00548    }
00549 
00550    return -1;
00551 }
00552 
00553 struct update2_prepare_struct {
00554    const char *database;
00555    const char *table;
00556    va_list ap;
00557 };
00558 
00559 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
00560 {
00561    int res, x = 1, first = 1;
00562    struct update2_prepare_struct *ups = data;
00563    const char *newparam, *newval;
00564    struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
00565    SQLHSTMT stmt;
00566    va_list ap;
00567    struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
00568    struct odbc_cache_columns *column;
00569 
00570    if (!sql) {
00571       if (tableptr) {
00572          ast_odbc_release_table(tableptr);
00573       }
00574       return NULL;
00575    }
00576 
00577    if (!tableptr) {
00578       ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'.  Update will fail!\n", ups->table, ups->database);
00579       return NULL;
00580    }
00581 
00582    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00583    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00584       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00585       ast_odbc_release_table(tableptr);
00586       return NULL;
00587    }
00588 
00589    ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
00590 
00591    /* Start by finding the second set of parameters */
00592    va_copy(ap, ups->ap);
00593 
00594    while ((newparam = va_arg(ap, const char *))) {
00595       newval = va_arg(ap, const char *);
00596    }
00597 
00598    while ((newparam = va_arg(ap, const char *))) {
00599       newval = va_arg(ap, const char *);
00600       if ((column = ast_odbc_find_column(tableptr, newparam))) {
00601          ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
00602          SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00603          first = 0;
00604       } else {
00605          ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
00606       }
00607    }
00608    va_end(ap);
00609 
00610    /* Restart search, because we need to add the search parameters */
00611    va_copy(ap, ups->ap);
00612    ast_str_append(&sql, 0, "WHERE");
00613    first = 1;
00614 
00615    while ((newparam = va_arg(ap, const char *))) {
00616       newval = va_arg(ap, const char *);
00617       if (!(column = ast_odbc_find_column(tableptr, newparam))) {
00618          ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", newparam, ups->table, ups->database);
00619          ast_odbc_release_table(tableptr);
00620          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00621          return NULL;
00622       }
00623       ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
00624       SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00625       first = 0;
00626    }
00627    va_end(ap);
00628 
00629    /* Done with the table metadata */
00630    ast_odbc_release_table(tableptr);
00631 
00632    res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
00633    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00634       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
00635       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00636       return NULL;
00637    }
00638 
00639    return stmt;
00640 }
00641 
00642 /*!
00643  * \brief Execute an UPDATE query
00644  * \param database
00645  * \param table
00646  * \param ap list containing one or more field/value set(s).
00647  *
00648  * Update a database table, preparing the sql statement from a list of
00649  * key/value pairs specified in ap.  The lookup pairs are specified first
00650  * and are separated from the update pairs by a sentinel value.
00651  * Sub-in the values to the prepared statement and execute it.
00652  *
00653  * \retval number of rows affected
00654  * \retval -1 on failure
00655 */
00656 static int update2_odbc(const char *database, const char *table, va_list ap)
00657 {
00658    struct odbc_obj *obj;
00659    SQLHSTMT stmt;
00660    struct update2_prepare_struct ups = { .database = database, .table = table, };
00661    struct ast_str *sql;
00662    int res;
00663    SQLLEN rowcount = 0;
00664 
00665    va_copy(ups.ap, ap);
00666 
00667    if (!(obj = ast_odbc_request_obj(database, 0))) {
00668       return -1;
00669    }
00670 
00671    if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
00672       ast_odbc_release_obj(obj);
00673       return -1;
00674    }
00675 
00676    res = SQLRowCount(stmt, &rowcount);
00677    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00678    ast_odbc_release_obj(obj);
00679 
00680    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00681       /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
00682       sql = ast_str_thread_get(&sql_buf, 16);
00683       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
00684       return -1;
00685    }
00686 
00687    if (rowcount >= 0) {
00688       return (int)rowcount;
00689    }
00690 
00691    return -1;
00692 }
00693 
00694 /*!
00695  * \brief Excute an INSERT query
00696  * \param database
00697  * \param table
00698  * \param ap list containing one or more field/value set(s)
00699  *
00700  * Insert a new record into database table, prepare the sql statement.
00701  * All values to be changed are stored in ap list.
00702  * Sub-in the values to the prepared statement and execute it.
00703  *
00704  * \retval number of rows affected
00705  * \retval -1 on failure
00706 */
00707 static int store_odbc(const char *database, const char *table, va_list ap)
00708 {
00709    struct odbc_obj *obj;
00710    SQLHSTMT stmt;
00711    char sql[256];
00712    char keys[256];
00713    char vals[256];
00714    SQLLEN rowcount=0;
00715    const char *newparam, *newval;
00716    int res;
00717    va_list aq;
00718    struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
00719 
00720    va_copy(cps.ap, ap);
00721    va_copy(aq, ap);
00722    
00723    if (!table)
00724       return -1;
00725 
00726    obj = ast_odbc_request_obj(database, 0);
00727    if (!obj)
00728       return -1;
00729 
00730    newparam = va_arg(aq, const char *);
00731    if (!newparam)  {
00732       ast_odbc_release_obj(obj);
00733       return -1;
00734    }
00735    newval = va_arg(aq, const char *);
00736    snprintf(keys, sizeof(keys), "%s", newparam);
00737    ast_copy_string(vals, "?", sizeof(vals));
00738    while ((newparam = va_arg(aq, const char *))) {
00739       snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
00740       snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
00741       newval = va_arg(aq, const char *);
00742    }
00743    va_end(aq);
00744    snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
00745 
00746    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00747 
00748    if (!stmt) {
00749       ast_odbc_release_obj(obj);
00750       return -1;
00751    }
00752 
00753    res = SQLRowCount(stmt, &rowcount);
00754    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00755    ast_odbc_release_obj(obj);
00756 
00757    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00758       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00759       return -1;
00760    }
00761 
00762    if (rowcount >= 0)
00763       return (int)rowcount;
00764 
00765    return -1;
00766 }
00767 
00768 /*!
00769  * \brief Excute an DELETE query
00770  * \param database
00771  * \param table
00772  * \param keyfield where clause field
00773  * \param lookup value of field for where clause
00774  * \param ap list containing one or more field/value set(s)
00775  *
00776  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
00777  * control the number of records to change. Additional params to match rows are stored in ap list.
00778  * Sub-in the values to the prepared statement and execute it.
00779  *
00780  * \retval number of rows affected
00781  * \retval -1 on failure
00782 */
00783 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00784 {
00785    struct odbc_obj *obj;
00786    SQLHSTMT stmt;
00787    char sql[256];
00788    SQLLEN rowcount=0;
00789    const char *newparam, *newval;
00790    int res;
00791    va_list aq;
00792    struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00793 
00794    va_copy(cps.ap, ap);
00795    va_copy(aq, ap);
00796    
00797    if (!table)
00798       return -1;
00799 
00800    obj = ast_odbc_request_obj(database, 0);
00801    if (!obj)
00802       return -1;
00803 
00804    snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
00805    while((newparam = va_arg(aq, const char *))) {
00806       snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
00807       newval = va_arg(aq, const char *);
00808    }
00809    va_end(aq);
00810    snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
00811 
00812    stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00813 
00814    if (!stmt) {
00815       ast_odbc_release_obj(obj);
00816       return -1;
00817    }
00818 
00819    res = SQLRowCount(stmt, &rowcount);
00820    SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00821    ast_odbc_release_obj(obj);
00822 
00823    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00824       ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00825       return -1;
00826    }
00827 
00828    if (rowcount >= 0)
00829       return (int)rowcount;
00830 
00831    return -1;
00832 }
00833 
00834 
00835 struct config_odbc_obj {
00836    char *sql;
00837    unsigned long cat_metric;
00838    char category[128];
00839    char var_name[128];
00840    char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
00841    SQLLEN err;
00842 };
00843 
00844 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
00845 {
00846    struct config_odbc_obj *q = data;
00847    SQLHSTMT sth;
00848    int res;
00849 
00850    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
00851    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00852       ast_verb(4, "Failure in AllocStatement %d\n", res);
00853       return NULL;
00854    }
00855 
00856    res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
00857    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00858       ast_verb(4, "Error in PREPARE %d\n", res);
00859       SQLFreeHandle(SQL_HANDLE_STMT, sth);
00860       return NULL;
00861    }
00862 
00863    SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
00864    SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
00865    SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
00866    SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
00867 
00868    return sth;
00869 }
00870 
00871 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl, const char *who_asked)
00872 {
00873    struct ast_variable *new_v;
00874    struct ast_category *cur_cat;
00875    int res = 0;
00876    struct odbc_obj *obj;
00877    char sqlbuf[1024] = "";
00878    char *sql = sqlbuf;
00879    size_t sqlleft = sizeof(sqlbuf);
00880    unsigned int last_cat_metric = 0;
00881    SQLSMALLINT rowcount = 0;
00882    SQLHSTMT stmt;
00883    char last[128] = "";
00884    struct config_odbc_obj q;
00885    struct ast_flags loader_flags = { 0 };
00886 
00887    memset(&q, 0, sizeof(q));
00888 
00889    if (!file || !strcmp (file, "res_config_odbc.conf"))
00890       return NULL;      /* cant configure myself with myself ! */
00891 
00892    obj = ast_odbc_request_obj(database, 0);
00893    if (!obj)
00894       return NULL;
00895 
00896    ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
00897    ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
00898    ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
00899    q.sql = sqlbuf;
00900 
00901    stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
00902 
00903    if (!stmt) {
00904       ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
00905       ast_odbc_release_obj(obj);
00906       return NULL;
00907    }
00908 
00909    res = SQLNumResultCols(stmt, &rowcount);
00910 
00911    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00912       ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
00913       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00914       ast_odbc_release_obj(obj);
00915       return NULL;
00916    }
00917 
00918    if (!rowcount) {
00919       ast_log(LOG_NOTICE, "found nothing\n");
00920       ast_odbc_release_obj(obj);
00921       return cfg;
00922    }
00923 
00924    cur_cat = ast_config_get_current_category(cfg);
00925 
00926    while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
00927       if (!strcmp (q.var_name, "#include")) {
00928          if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
00929             SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00930             ast_odbc_release_obj(obj);
00931             return NULL;
00932          }
00933          continue;
00934       } 
00935       if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
00936          cur_cat = ast_category_new(q.category, "", 99999);
00937          if (!cur_cat) {
00938             ast_log(LOG_WARNING, "Out of memory!\n");
00939             break;
00940          }
00941          strcpy(last, q.category);
00942          last_cat_metric   = q.cat_metric;
00943          ast_category_append(cfg, cur_cat);
00944       }
00945 
00946       new_v = ast_variable_new(q.var_name, q.var_val, "");
00947       ast_variable_append(cur_cat, new_v);
00948    }
00949 
00950    SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00951    ast_odbc_release_obj(obj);
00952    return cfg;
00953 }
00954 
00955 #define warn_length(col, size)   ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is not long enough to contain realtime data (needs %d)\n", table, database, col->name, size)
00956 #define warn_type(col, type)  ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is of the incorrect type (%d) to contain the required realtime data\n", table, database, col->name, col->type)
00957 
00958 static int require_odbc(const char *database, const char *table, va_list ap)
00959 {
00960    struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
00961    struct odbc_cache_columns *col;
00962    char *elm;
00963    int type, size;
00964 
00965    if (!tableptr) {
00966       return -1;
00967    }
00968 
00969    while ((elm = va_arg(ap, char *))) {
00970       type = va_arg(ap, require_type);
00971       size = va_arg(ap, int);
00972       /* Check if the field matches the criteria */
00973       AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
00974          if (strcmp(col->name, elm) == 0) {
00975             /* Type check, first.  Some fields are more particular than others */
00976             switch (col->type) {
00977             case SQL_CHAR:
00978             case SQL_VARCHAR:
00979             case SQL_LONGVARCHAR:
00980 #ifdef HAVE_ODBC_WCHAR
00981             case SQL_WCHAR:
00982             case SQL_WVARCHAR:
00983             case SQL_WLONGVARCHAR:
00984 #endif
00985             case SQL_BINARY:
00986             case SQL_VARBINARY:
00987             case SQL_LONGVARBINARY:
00988             case SQL_GUID:
00989 #define CHECK_SIZE(n) \
00990                   if (col->size < n) {      \
00991                      warn_length(col, n);  \
00992                   }                         \
00993                   break;
00994                switch (type) {
00995                case RQ_UINTEGER1: CHECK_SIZE(3)  /*         255 */
00996                case RQ_INTEGER1:  CHECK_SIZE(4)  /*        -128 */
00997                case RQ_UINTEGER2: CHECK_SIZE(5)  /*       65535 */
00998                case RQ_INTEGER2:  CHECK_SIZE(6)  /*      -32768 */
00999                case RQ_UINTEGER3:                /*    16777215 */
01000                case RQ_INTEGER3:  CHECK_SIZE(8)  /*    -8388608 */
01001                case RQ_DATE:                     /*  2008-06-09 */
01002                case RQ_UINTEGER4: CHECK_SIZE(10) /*  4200000000 */
01003                case RQ_INTEGER4:  CHECK_SIZE(11) /* -2100000000 */
01004                case RQ_DATETIME:                 /* 2008-06-09 16:03:47 */
01005                case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me    */
01006                case RQ_INTEGER8:  CHECK_SIZE(20) /* ditto       */
01007                case RQ_FLOAT:
01008                case RQ_CHAR:      CHECK_SIZE(size)
01009                }
01010 #undef CHECK_SIZE
01011                break;
01012             case SQL_TYPE_DATE:
01013                if (type != RQ_DATE) {
01014                   warn_type(col, type);
01015                }
01016                break;
01017             case SQL_TYPE_TIMESTAMP:
01018             case SQL_TIMESTAMP:
01019                if (type != RQ_DATE && type != RQ_DATETIME) {
01020                   warn_type(col, type);
01021                }
01022                break;
01023             case SQL_BIT:
01024                warn_length(col, size);
01025                break;
01026 #define WARN_TYPE_OR_LENGTH(n)   \
01027                   if (!ast_rq_is_int(type)) {  \
01028                      warn_type(col, type);    \
01029                   } else {                     \
01030                      warn_length(col, n);  \
01031                   }
01032             case SQL_TINYINT:
01033                if (type != RQ_UINTEGER1) {
01034                   WARN_TYPE_OR_LENGTH(size)
01035                }
01036                break;
01037             case SQL_C_STINYINT:
01038                if (type != RQ_INTEGER1) {
01039                   WARN_TYPE_OR_LENGTH(size)
01040                }
01041                break;
01042             case SQL_C_USHORT:
01043                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
01044                   WARN_TYPE_OR_LENGTH(size)
01045                }
01046                break;
01047             case SQL_SMALLINT:
01048             case SQL_C_SSHORT:
01049                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
01050                   WARN_TYPE_OR_LENGTH(size)
01051                }
01052                break;
01053             case SQL_C_ULONG:
01054                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01055                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01056                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01057                   type != RQ_INTEGER4) {
01058                   WARN_TYPE_OR_LENGTH(size)
01059                }
01060                break;
01061             case SQL_INTEGER:
01062             case SQL_C_SLONG:
01063                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01064                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01065                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01066                   type != RQ_INTEGER4) {
01067                   WARN_TYPE_OR_LENGTH(size)
01068                }
01069                break;
01070             case SQL_C_UBIGINT:
01071                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01072                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01073                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01074                   type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
01075                   type != RQ_INTEGER8) {
01076                   WARN_TYPE_OR_LENGTH(size)
01077                }
01078                break;
01079             case SQL_BIGINT:
01080             case SQL_C_SBIGINT:
01081                if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
01082                   type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
01083                   type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
01084                   type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
01085                   type != RQ_INTEGER8) {
01086                   WARN_TYPE_OR_LENGTH(size)
01087                }
01088                break;
01089 #undef WARN_TYPE_OR_LENGTH
01090             case SQL_NUMERIC:
01091             case SQL_DECIMAL:
01092             case SQL_FLOAT:
01093             case SQL_REAL:
01094             case SQL_DOUBLE:
01095                if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
01096                   warn_type(col, type);
01097                }
01098                break;
01099             default:
01100                ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
01101             }
01102             break;
01103          }
01104       }
01105       if (!col) {
01106          ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
01107       }
01108    }
01109    va_end(ap);
01110    AST_RWLIST_UNLOCK(&tableptr->columns);
01111    return 0;
01112 }
01113 #undef warn_length
01114 #undef warn_type
01115 
01116 static struct ast_config_engine odbc_engine = {
01117    .name = "odbc",
01118    .load_func = config_odbc,
01119    .realtime_func = realtime_odbc,
01120    .realtime_multi_func = realtime_multi_odbc,
01121    .store_func = store_odbc,
01122    .destroy_func = destroy_odbc,
01123    .update_func = update_odbc,
01124    .update2_func = update2_odbc,
01125    .require_func = require_odbc,
01126    .unload_func = ast_odbc_clear_cache,
01127 };
01128 
01129 static int unload_module (void)
01130 {
01131    ast_config_engine_deregister(&odbc_engine);
01132 
01133    ast_verb(1, "res_config_odbc unloaded.\n");
01134    return 0;
01135 }
01136 
01137 static int load_module (void)
01138 {
01139    ast_config_engine_register(&odbc_engine);
01140    ast_verb(1, "res_config_odbc loaded.\n");
01141    return 0;
01142 }
01143 
01144 static int reload_module(void)
01145 {
01146    return 0;
01147 }
01148 
01149 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime ODBC configuration",
01150       .load = load_module,
01151       .unload = unload_module,
01152       .reload = reload_module,
01153       );