diff options
Diffstat (limited to 'res/res_config_odbc.c')
-rw-r--r-- | res/res_config_odbc.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c new file mode 100644 index 000000000..7f11b5db3 --- /dev/null +++ b/res/res_config_odbc.c @@ -0,0 +1,564 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief odbc+odbc plugin for portable configuration engine + * + * \author Mark Spencer <markster@digium.com> + * \author Anthony Minessale II <anthmct@yahoo.com> + * + * \arg http://www.unixodbc.org + */ + +/*** MODULEINFO + <depend>unixodbc</depend> + <depend>ltdl</depend> + <depend>res_odbc</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "asterisk/file.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/config.h" +#include "asterisk/module.h" +#include "asterisk/lock.h" +#include "asterisk/options.h" +#include "asterisk/res_odbc.h" +#include "asterisk/utils.h" + +struct custom_prepare_struct { + const char *sql; + const char *extra; + va_list ap; +}; + +static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data) +{ + int res, x = 1; + struct custom_prepare_struct *cps = data; + const char *newparam, *newval; + SQLHSTMT stmt; + va_list ap; + + va_copy(ap, cps->ap); + + res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n"); + return NULL; + } + + res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql); + SQLFreeHandle (SQL_HANDLE_STMT, stmt); + return NULL; + } + + while ((newparam = va_arg(ap, const char *))) { + newval = va_arg(ap, const char *); + SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL); + } + va_end(ap); + + if (!ast_strlen_zero(cps->extra)) + SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL); + return stmt; +} + +static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap) +{ + struct odbc_obj *obj; + SQLHSTMT stmt; + char sql[1024]; + char coltitle[256]; + char rowdata[2048]; + char *op; + const char *newparam, *newval; + char *stringp; + char *chunk; + SQLSMALLINT collen; + int res; + int x; + struct ast_variable *var=NULL, *prev=NULL; + SQLULEN colsize; + SQLSMALLINT colcount=0; + SQLSMALLINT datatype; + SQLSMALLINT decimaldigits; + SQLSMALLINT nullable; + SQLLEN indicator; + va_list aq; + struct custom_prepare_struct cps = { .sql = sql }; + + va_copy(cps.ap, ap); + va_copy(aq, ap); + + if (!table) + return NULL; + + obj = ast_odbc_request_obj(database, 0); + + if (!obj) { + ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database); + return NULL; + } + + newparam = va_arg(aq, const char *); + if (!newparam) { + ast_odbc_release_obj(obj); + return NULL; + } + newval = va_arg(aq, const char *); + op = !strchr(newparam, ' ') ? " =" : ""; + snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op, + strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : ""); + while((newparam = va_arg(aq, const char *))) { + op = !strchr(newparam, ' ') ? " =" : ""; + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op, + strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : ""); + newval = va_arg(aq, const char *); + } + va_end(aq); + + stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps); + + if (!stmt) { + ast_odbc_release_obj(obj); + return NULL; + } + + res = SQLNumResultCols(stmt, &colcount); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql); + SQLFreeHandle (SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + + res = SQLFetch(stmt); + if (res == SQL_NO_DATA) { + SQLFreeHandle (SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql); + SQLFreeHandle (SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + for (x = 0; x < colcount; x++) { + rowdata[0] = '\0'; + collen = sizeof(coltitle); + res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, + &datatype, &colsize, &decimaldigits, &nullable); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql); + if (var) + ast_variables_destroy(var); + ast_odbc_release_obj(obj); + return NULL; + } + + indicator = 0; + res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator); + if (indicator == SQL_NULL_DATA) + continue; + + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql); + if (var) + ast_variables_destroy(var); + ast_odbc_release_obj(obj); + return NULL; + } + stringp = rowdata; + while(stringp) { + chunk = strsep(&stringp, ";"); + if (!ast_strlen_zero(ast_strip(chunk))) { + if (prev) { + prev->next = ast_variable_new(coltitle, chunk); + if (prev->next) + prev = prev->next; + } else + prev = var = ast_variable_new(coltitle, chunk); + } + } + } + + + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return var; +} + +static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap) +{ + struct odbc_obj *obj; + SQLHSTMT stmt; + char sql[1024]; + char coltitle[256]; + char rowdata[2048]; + const char *initfield=NULL; + char *op; + const char *newparam, *newval; + char *stringp; + char *chunk; + SQLSMALLINT collen; + int res; + int x; + struct ast_variable *var=NULL; + struct ast_config *cfg=NULL; + struct ast_category *cat=NULL; + struct ast_realloca ra; + SQLULEN colsize; + SQLSMALLINT colcount=0; + SQLSMALLINT datatype; + SQLSMALLINT decimaldigits; + SQLSMALLINT nullable; + SQLLEN indicator; + struct custom_prepare_struct cps = { .sql = sql }; + va_list aq; + + va_copy(cps.ap, ap); + va_copy(aq, ap); + + if (!table) + return NULL; + memset(&ra, 0, sizeof(ra)); + + obj = ast_odbc_request_obj(database, 0); + if (!obj) + return NULL; + + newparam = va_arg(aq, const char *); + if (!newparam) { + ast_odbc_release_obj(obj); + return NULL; + } + initfield = ast_strdupa(newparam); + if ((op = strchr(initfield, ' '))) + *op = '\0'; + newval = va_arg(aq, const char *); + op = !strchr(newparam, ' ') ? " =" : ""; + snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op, + strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : ""); + while((newparam = va_arg(aq, const char *))) { + op = !strchr(newparam, ' ') ? " =" : ""; + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op, + strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : ""); + newval = va_arg(aq, const char *); + } + if (initfield) + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield); + va_end(aq); + + stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps); + + if (!stmt) { + ast_odbc_release_obj(obj); + return NULL; + } + + res = SQLNumResultCols(stmt, &colcount); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + + cfg = ast_config_new(); + if (!cfg) { + ast_log(LOG_WARNING, "Out of memory!\n"); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + + while ((res=SQLFetch(stmt)) != SQL_NO_DATA) { + var = NULL; + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql); + continue; + } + cat = ast_category_new(""); + if (!cat) { + ast_log(LOG_WARNING, "Out of memory!\n"); + continue; + } + for (x=0;x<colcount;x++) { + rowdata[0] = '\0'; + collen = sizeof(coltitle); + res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, + &datatype, &colsize, &decimaldigits, &nullable); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql); + ast_category_destroy(cat); + continue; + } + + indicator = 0; + res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator); + if (indicator == SQL_NULL_DATA) + continue; + + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql); + ast_category_destroy(cat); + continue; + } + stringp = rowdata; + while(stringp) { + chunk = strsep(&stringp, ";"); + if (!ast_strlen_zero(ast_strip(chunk))) { + if (initfield && !strcmp(initfield, coltitle)) + ast_category_rename(cat, chunk); + var = ast_variable_new(coltitle, chunk); + ast_variable_append(cat, var); + } + } + } + ast_category_append(cfg, cat); + } + + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return cfg; +} + +static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap) +{ + struct odbc_obj *obj; + SQLHSTMT stmt; + char sql[256]; + SQLLEN rowcount=0; + const char *newparam, *newval; + int res; + va_list aq; + struct custom_prepare_struct cps = { .sql = sql, .extra = lookup }; + + va_copy(cps.ap, ap); + va_copy(aq, ap); + + if (!table) + return -1; + + obj = ast_odbc_request_obj(database, 0); + if (!obj) + return -1; + + newparam = va_arg(aq, const char *); + if (!newparam) { + ast_odbc_release_obj(obj); + return -1; + } + newval = va_arg(aq, const char *); + snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam); + while((newparam = va_arg(aq, const char *))) { + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam); + newval = va_arg(aq, const char *); + } + va_end(aq); + snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield); + + stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps); + + if (!stmt) { + ast_odbc_release_obj(obj); + return -1; + } + + res = SQLRowCount(stmt, &rowcount); + SQLFreeHandle (SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql); + return -1; + } + + if (rowcount >= 0) + return (int)rowcount; + + return -1; +} + +struct config_odbc_obj { + char *sql; + unsigned long cat_metric; + char category[128]; + char var_name[128]; + char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */ + SQLLEN err; +}; + +static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data) +{ + struct config_odbc_obj *q = data; + SQLHSTMT sth; + int res; + + res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 3) + ast_verbose( VERBOSE_PREFIX_4 "Failure in AllocStatement %d\n", res); + return NULL; + } + + res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + if (option_verbose > 3) + ast_verbose( VERBOSE_PREFIX_4 "Error in PREPARE %d\n", res); + SQLFreeHandle(SQL_HANDLE_STMT, sth); + return NULL; + } + + SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err); + SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err); + SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err); + SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err); + + return sth; +} + +static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments) +{ + struct ast_variable *new_v; + struct ast_category *cur_cat; + int res = 0; + struct odbc_obj *obj; + char sqlbuf[1024] = ""; + char *sql = sqlbuf; + size_t sqlleft = sizeof(sqlbuf); + unsigned int last_cat_metric = 0; + SQLSMALLINT rowcount = 0; + SQLHSTMT stmt; + char last[128] = ""; + struct config_odbc_obj q; + + memset(&q, 0, sizeof(q)); + + if (!file || !strcmp (file, "res_config_odbc.conf")) + return NULL; /* cant configure myself with myself ! */ + + obj = ast_odbc_request_obj(database, 0); + if (!obj) + return NULL; + + ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table); + ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file); + ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name "); + q.sql = sqlbuf; + + stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q); + + if (!stmt) { + ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql); + ast_odbc_release_obj(obj); + return NULL; + } + + res = SQLNumResultCols(stmt, &rowcount); + + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + + if (!rowcount) { + ast_log(LOG_NOTICE, "found nothing\n"); + ast_odbc_release_obj(obj); + return cfg; + } + + cur_cat = ast_config_get_current_category(cfg); + + while ((res = SQLFetch(stmt)) != SQL_NO_DATA) { + if (!strcmp (q.var_name, "#include")) { + if (!ast_config_internal_load(q.var_val, cfg, 0)) { + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + continue; + } + if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) { + cur_cat = ast_category_new(q.category); + if (!cur_cat) { + ast_log(LOG_WARNING, "Out of memory!\n"); + break; + } + strcpy(last, q.category); + last_cat_metric = q.cat_metric; + ast_category_append(cfg, cur_cat); + } + + new_v = ast_variable_new(q.var_name, q.var_val); + ast_variable_append(cur_cat, new_v); + } + + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return cfg; +} + +static struct ast_config_engine odbc_engine = { + .name = "odbc", + .load_func = config_odbc, + .realtime_func = realtime_odbc, + .realtime_multi_func = realtime_multi_odbc, + .update_func = update_odbc +}; + +static int unload_module (void) +{ + ast_module_user_hangup_all(); + ast_config_engine_deregister(&odbc_engine); + if (option_verbose) + ast_verbose("res_config_odbc unloaded.\n"); + return 0; +} + +static int load_module (void) +{ + ast_config_engine_register(&odbc_engine); + if (option_verbose) + ast_verbose("res_config_odbc loaded.\n"); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC Configuration", + .load = load_module, + .unload = unload_module, + ); |