2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
23 * \brief odbc+odbc plugin for portable configuration engine
25 * \author Mark Spencer <markster@digium.com>
26 * \author Anthony Minessale II <anthmct@yahoo.com>
28 * \arg http://www.unixodbc.org
32 <depend>unixodbc</depend>
33 <depend>res_odbc</depend>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
45 #include "asterisk/file.h"
46 #include "asterisk/logger.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/config.h"
50 #include "asterisk/module.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/options.h"
53 #include "asterisk/res_odbc.h"
54 #include "asterisk/utils.h"
56 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
64 const char *newparam, *newval;
70 struct ast_variable *var=NULL, *prev=NULL;
72 SQLSMALLINT colcount=0;
74 SQLSMALLINT decimaldigits;
85 obj = ast_odbc_request_obj(database, 0);
89 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
90 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
91 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
92 ast_odbc_release_obj(obj);
96 newparam = va_arg(aq, const char *);
98 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
99 ast_odbc_release_obj(obj);
102 newval = va_arg(aq, const char *);
103 if (!strchr(newparam, ' ')) op = " ="; else op = "";
104 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?", table, newparam, op);
105 while((newparam = va_arg(aq, const char *))) {
106 if (!strchr(newparam, ' ')) op = " ="; else op = "";
107 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?", newparam, op);
108 newval = va_arg(aq, const char *);
111 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
112 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
113 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
114 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
115 ast_odbc_release_obj(obj);
119 /* Now bind the parameters */
122 while((newparam = va_arg(ap, const char *))) {
123 newval = va_arg(ap, const char *);
124 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
127 res = ast_odbc_smart_execute(obj, stmt);
129 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
130 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
131 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
132 ast_odbc_release_obj(obj);
136 res = SQLNumResultCols(stmt, &colcount);
137 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
138 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
139 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
140 ast_odbc_release_obj(obj);
144 res = SQLFetch(stmt);
145 if (res == SQL_NO_DATA) {
146 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
147 ast_odbc_release_obj(obj);
150 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
151 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
152 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
153 ast_odbc_release_obj(obj);
156 for (x = 0; x < colcount; x++) {
158 collen = sizeof(coltitle);
159 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
160 &datatype, &colsize, &decimaldigits, &nullable);
161 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
162 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
164 ast_variables_destroy(var);
165 ast_odbc_release_obj(obj);
170 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
171 if (indicator == SQL_NULL_DATA)
173 else if (ast_strlen_zero(rowdata)) {
174 /* Because we encode the empty string for a NULL, we will encode
175 * actual empty strings as a string containing a single whitespace. */
176 ast_copy_string(rowdata, " ", sizeof(rowdata));
179 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
180 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
182 ast_variables_destroy(var);
183 ast_odbc_release_obj(obj);
188 chunk = strsep(&stringp, ";");
190 prev->next = ast_variable_new(coltitle, chunk);
194 prev = var = ast_variable_new(coltitle, chunk);
199 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
200 ast_odbc_release_obj(obj);
204 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
206 struct odbc_obj *obj;
211 const char *initfield=NULL;
213 const char *newparam, *newval;
219 struct ast_variable *var=NULL;
220 struct ast_config *cfg=NULL;
221 struct ast_category *cat=NULL;
222 struct ast_realloca ra;
224 SQLSMALLINT colcount=0;
225 SQLSMALLINT datatype;
226 SQLSMALLINT decimaldigits;
227 SQLSMALLINT nullable;
236 memset(&ra, 0, sizeof(ra));
238 obj = ast_odbc_request_obj(database, 0);
242 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
243 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
244 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
245 ast_odbc_release_obj(obj);
249 newparam = va_arg(aq, const char *);
251 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
252 ast_odbc_release_obj(obj);
255 initfield = ast_strdupa(newparam);
256 if ((op = strchr(initfield, ' ')))
258 newval = va_arg(aq, const char *);
259 if (!strchr(newparam, ' ')) op = " ="; else op = "";
260 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?", table, newparam, op);
261 while((newparam = va_arg(aq, const char *))) {
262 if (!strchr(newparam, ' ')) op = " ="; else op = "";
263 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?", newparam, op);
264 newval = va_arg(aq, const char *);
267 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
269 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
270 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
271 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
272 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
273 ast_odbc_release_obj(obj);
277 /* Now bind the parameters */
280 while((newparam = va_arg(ap, const char *))) {
281 newval = va_arg(ap, const char *);
282 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
285 res = ast_odbc_smart_execute(obj, stmt);
287 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
288 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
289 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
290 ast_odbc_release_obj(obj);
294 res = SQLNumResultCols(stmt, &colcount);
295 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
296 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
297 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
298 ast_odbc_release_obj(obj);
302 cfg = ast_config_new();
304 ast_log(LOG_WARNING, "Out of memory!\n");
305 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
306 ast_odbc_release_obj(obj);
310 while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
312 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
313 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
316 cat = ast_category_new("");
318 ast_log(LOG_WARNING, "Out of memory!\n");
321 for (x=0;x<colcount;x++) {
323 collen = sizeof(coltitle);
324 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
325 &datatype, &colsize, &decimaldigits, &nullable);
326 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
327 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
328 ast_category_destroy(cat);
333 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
334 if (indicator == SQL_NULL_DATA)
337 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
338 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
339 ast_category_destroy(cat);
344 chunk = strsep(&stringp, ";");
345 if (!ast_strlen_zero(ast_strip(chunk))) {
346 if (initfield && !strcmp(initfield, coltitle))
347 ast_category_rename(cat, chunk);
348 var = ast_variable_new(coltitle, chunk);
349 ast_variable_append(cat, var);
353 ast_category_append(cfg, cat);
356 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
357 ast_odbc_release_obj(obj);
361 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
363 struct odbc_obj *obj;
367 const char *newparam, *newval;
377 obj = ast_odbc_request_obj(database, 0);
381 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
382 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
383 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
384 ast_odbc_release_obj(obj);
388 newparam = va_arg(aq, const char *);
390 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
391 ast_odbc_release_obj(obj);
394 newval = va_arg(aq, const char *);
395 snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
396 while((newparam = va_arg(aq, const char *))) {
397 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
398 newval = va_arg(aq, const char *);
401 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
403 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
404 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
405 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
406 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
407 ast_odbc_release_obj(obj);
411 /* Now bind the parameters */
414 while((newparam = va_arg(ap, const char *))) {
415 newval = va_arg(ap, const char *);
416 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
419 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(lookup), 0, (void *)lookup, 0, NULL);
421 res = ast_odbc_smart_execute(obj, stmt);
423 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
424 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
425 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
426 ast_odbc_release_obj(obj);
430 res = SQLRowCount(stmt, &rowcount);
431 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
432 ast_odbc_release_obj(obj);
434 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
435 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
440 return (int)rowcount;
445 struct config_odbc_obj {
447 unsigned long cat_metric;
450 char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
454 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
456 struct config_odbc_obj *q = data;
460 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
461 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
462 if (option_verbose > 3)
463 ast_verbose( VERBOSE_PREFIX_4 "Failure in AllocStatement %d\n", res);
467 res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
468 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
469 if (option_verbose > 3)
470 ast_verbose( VERBOSE_PREFIX_4 "Error in PREPARE %d\n", res);
471 SQLFreeHandle(SQL_HANDLE_STMT, sth);
475 SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
476 SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
477 SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
478 SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
483 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments)
485 struct ast_variable *new_v;
486 struct ast_category *cur_cat;
488 struct odbc_obj *obj;
489 char sqlbuf[1024] = "";
491 size_t sqlleft = sizeof(sqlbuf);
492 unsigned int last_cat_metric = 0;
493 SQLSMALLINT rowcount = 0;
496 struct config_odbc_obj q;
498 memset(&q, 0, sizeof(q));
500 if (!file || !strcmp (file, "res_config_odbc.conf"))
501 return NULL; /* cant configure myself with myself ! */
503 obj = ast_odbc_request_obj(database, 0);
507 ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
508 ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
509 ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
512 stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
515 ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
516 ast_odbc_release_obj(obj);
520 res = SQLNumResultCols(stmt, &rowcount);
522 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
523 ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
524 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
525 ast_odbc_release_obj(obj);
530 ast_log(LOG_NOTICE, "found nothing\n");
531 ast_odbc_release_obj(obj);
535 cur_cat = ast_config_get_current_category(cfg);
537 while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
538 if (!strcmp (q.var_name, "#include")) {
539 if (!ast_config_internal_load(q.var_val, cfg, 0)) {
540 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
541 ast_odbc_release_obj(obj);
546 if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
547 cur_cat = ast_category_new(q.category);
549 ast_log(LOG_WARNING, "Out of memory!\n");
552 strcpy(last, q.category);
553 last_cat_metric = q.cat_metric;
554 ast_category_append(cfg, cur_cat);
557 new_v = ast_variable_new(q.var_name, q.var_val);
558 ast_variable_append(cur_cat, new_v);
561 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
562 ast_odbc_release_obj(obj);
566 static struct ast_config_engine odbc_engine = {
568 .load_func = config_odbc,
569 .realtime_func = realtime_odbc,
570 .realtime_multi_func = realtime_multi_odbc,
571 .update_func = update_odbc
574 static int unload_module (void)
576 ast_module_user_hangup_all();
577 ast_config_engine_deregister(&odbc_engine);
579 ast_verbose("res_config_odbc unloaded.\n");
583 static int load_module (void)
585 ast_config_engine_register(&odbc_engine);
587 ast_verbose("res_config_odbc loaded.\n");
591 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime ODBC configuration",
593 .unload = unload_module,