rendered paste body/* * Asterisk -- A telephony toolkit for Linux. * * func_odbc * * Copyright (c) 2005 Tilghman Lesher * * Tilghman Lesher <func_odbc__200508@the-tilghman.com> * */#include <sys/types.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <asterisk/file.h>#include <asterisk/logger.h>#include <asterisk/options.h>#include <asterisk/channel.h>#include <asterisk/pbx.h>#include <asterisk/module.h>#include <asterisk/config.h>#include <asterisk/res_odbc.h>static char *tdesc = "ODBC lookups";static char *config = "func_odbc.conf";struct acf_odbc_query { char name[30]; char dsn[30]; char sql[512]; struct ast_custom_function *acf; unsigned int deleteme:1; struct acf_odbc_query *next;};static struct acf_odbc_query *queries = NULL;AST_MUTEX_DEFINE_STATIC(query_lock);/* * Master control routine */static char *acf_odbc_exec(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len){ odbc_obj *obj; struct acf_odbc_query *query; char *s, *arg, sql[512] = ""; int count=0, res, x; struct ast_channel *ast; SQLHSTMT stmt; SQLSMALLINT colcount=0; SQLINTEGER indicator; ast_mutex_lock(&query_lock); for (query=queries; query; query = query->next) { if (!strcasecmp(query->name, cmd + 5)) { break; } } if (!query) { ast_log(LOG_ERROR, "No such function '%s'\n", cmd); return ""; } obj = fetch_odbc_obj(query->dsn, 0); /* Parse our arguments */ s = ast_strdupa(data); if (!s) { ast_log(LOG_ERROR, "Out of memory\n"); return ""; } /* Set up arguments as variables in a fake channel */ ast = ast_channel_alloc(0); while ((arg = strsep(&s, "|"))) { count++; snprintf(buf, len, "ARG%d", count); pbx_builtin_setvar_helper(ast, buf, arg); } pbx_substitute_variables_helper(ast, query->sql, sql, sizeof(sql) - 1); ast_channel_free(ast); ast_mutex_unlock(&query_lock); 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 ""; } res = SQLPrepare(stmt, buf, SQL_NTS); if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql); SQLFreeHandle (SQL_HANDLE_STMT, stmt); return ""; } res = odbc_smart_execute(obj, stmt); if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql); SQLFreeHandle (SQL_HANDLE_STMT, stmt); return ""; } 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); return ""; } memset(buf, 0, len); for (x=0; x<colcount; x++) { int buflen, coldatalen; char coldata[256]; buflen = strlen(buf); res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator); if (indicator == SQL_NULL_DATA) { coldata[0] = '\0'; res = SQL_SUCCESS; } if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql); SQLFreeHandle(SQL_HANDLE_STMT, stmt); return ""; } strncat(buf + buflen, coldata, len - buflen); coldatalen = strlen(coldata); strncat(buf + buflen + coldatalen, ",", len - buflen - coldatalen); } /* Trim trailing comma */ buf[strlen(buf) - 1] = '\0'; SQLFreeHandle(SQL_HANDLE_STMT, stmt); return buf;}static int odbc_load_module(void){ int res = 0; struct ast_config *cfg; char *tmp, *catg; ast_mutex_lock(&query_lock); cfg = ast_config_load(config); if (!cfg) { ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config); goto out; } for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) { int ok = 1; struct acf_odbc_query *query; query = calloc(1, sizeof(struct acf_odbc_query)); if (!query) { ast_log(LOG_ERROR, "Out of memory\n"); goto out2; } ast_copy_string(query->name, catg, sizeof(query->name) - 1); tmp = ast_variable_retrieve(cfg, catg, "dsn"); if (tmp) { ast_copy_string(query->dsn, tmp, sizeof(query->dsn) - 1); } else { ok = 0; } tmp = ast_variable_retrieve(cfg, catg, "sql"); if (tmp) { ast_copy_string(query->sql, tmp, sizeof(query->sql) - 1); } else { ok = 0; } query->acf = calloc(1, sizeof(struct ast_custom_function)); if (query->acf) { asprintf(&(query->acf->name), "ODBC_%s", catg); asprintf(&(query->acf->syntax), "ODBC_%s(<arg1>[...[,<argN>]])", catg); query->acf->synopsis = "Runs the referenced query with the specified arguments"; query->acf->desc = "Runs the referenced query, as defined in func_odbc.conf, performing substitution of the arguments\n" "into the query as specified by ${ARG1}, ${ARG2}, ... ${ARGn}\n"; query->acf->read = acf_odbc_exec; if (! query->acf->name || ! query->acf->syntax) { ok = 0; } } else { ok = 0; } if (ok) { query->next = queries; queries = query; ast_custom_function_register(query->acf); } else { ast_log(LOG_ERROR, "Out of memory\n"); free(query); } }out2: ast_config_destroy(cfg);out: ast_mutex_unlock(&query_lock); return res;}static int odbc_unload_module(void){ struct acf_odbc_query *query, *lastquery = NULL; ast_mutex_lock(&query_lock); for (query = queries; query; query = query->next) { if (lastquery) free(lastquery); if (ast_custom_function_unregister(query->acf)) { ast_log(LOG_ERROR, "PANIC! Cannot unregister function? System will be left in a weird state (Recommend restart)\n"); lastquery = NULL; } else { free(query->acf->name); free(query->acf->syntax); free(query->acf); lastquery = query; } } if (lastquery) free(lastquery); queries = NULL; ast_mutex_unlock(&query_lock); return 0;}int reload(void){ int res = 0, found=0; struct ast_config *cfg; struct acf_odbc_query *q, *prevq = NULL, *qdel = NULL; char *tmp, *catg; ast_mutex_lock(&query_lock); for (q = queries; q; q = q->next) { q->deleteme = 1; } cfg = ast_config_load(config); if (!cfg) { ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config); goto out; } for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) { int ok = 1; struct acf_odbc_query *query = NULL; for (q = queries; q; q = q->next) { if (!strcasecmp(catg, q->name)) { q->deleteme = 0; found = 1; break; } } if (!found) { query = calloc(1, sizeof(struct acf_odbc_query)); if (!query) { ast_log(LOG_ERROR, "Out of memory\n"); goto out2; } ast_copy_string(query->name, catg, sizeof(query->name) - 1); /* The ACF is kept within the structure, to prevent a memory leak */ query->acf = calloc(1, sizeof(struct ast_custom_function)); if (query->acf) { vasprintf(&(query->acf->name), "ODBC_%s", catg); vasprintf(&(query->acf->syntax), "ODBC_%s(<arg1>[...[,<argN>]])", catg); query->acf->synopsis = "Runs the referenced query with the specified arguments"; query->acf->desc = "Runs the referenced query, as defined in func_odbc.conf, performing substitution of the arguments\n" "into the query as specified by ${ARG1}, ${ARG2}, ... ${ARGn}\n"; query->acf->read = acf_odbc_exec; if (! query->acf->name || ! query->acf->syntax) { ok = 0; } } else { ok = 0; } if (ok) { query->next = queries; queries = query; ast_custom_function_register(query->acf); } else { ast_log(LOG_ERROR, "Out of memory\n"); free(query); } } /* Do this section whether it is new or not */ if (query) { tmp = ast_variable_retrieve(cfg, catg, "dsn"); if (tmp) { ast_copy_string(query->dsn, tmp, sizeof(query->dsn) - 1); } else { ok = 0; } tmp = ast_variable_retrieve(cfg, catg, "sql"); if (tmp) { ast_copy_string(query->sql, tmp, sizeof(query->sql) - 1); } else { ok = 0; } } } /* Loop back for another category */ /* Any remaining sets will now be destroyed */ for (q = queries; q; q = q->next) { if (qdel) { free(qdel); qdel = NULL; } if (q->deleteme) { if (ast_custom_function_unregister(q->acf)) { ast_log(LOG_ERROR, "Cannot unregister function? Refusing to make 'ODBC_%s' go away.\n", q->name); } else { free(q->acf->name); free(q->acf->syntax); free(q->acf); if (prevq) { prevq->next = q->next; } else { queries = q->next; } qdel = q; } } } if (qdel) free(qdel);out2: ast_config_destroy(cfg);out: ast_mutex_unlock(&query_lock); return res;}int unload_module(void){ return odbc_unload_module();}int load_module(void){ return odbc_load_module();}char *description(void){ return tdesc;}int usecount(void){ if (! ast_mutex_trylock(&query_lock)) { ast_mutex_unlock(&query_lock); return 0; } else { return 1; }}char *key(){ return ASTERISK_GPL_KEY;}