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>
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
44 #include "asterisk/file.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/config.h"
49 #include "asterisk/module.h"
50 #include "asterisk/lock.h"
51 #include "asterisk/options.h"
52 #include "asterisk/res_odbc.h"
53 #include "asterisk/utils.h"
55 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
63 const char *newparam, *newval;
69 struct ast_variable *var=NULL, *prev=NULL;
71 SQLSMALLINT colcount=0;
73 SQLSMALLINT decimaldigits;
84 obj = ast_odbc_request_obj(database, 0);
88 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
89 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
90 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
91 ast_odbc_release_obj(obj);
95 newparam = va_arg(aq, const char *);
97 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
98 ast_odbc_release_obj(obj);
101 newval = va_arg(aq, const char *);
102 if (!strchr(newparam, ' ')) op = " ="; else op = "";
103 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?", table, newparam, op);
104 while((newparam = va_arg(aq, const char *))) {
105 if (!strchr(newparam, ' ')) op = " ="; else op = "";
106 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?", newparam, op);
107 newval = va_arg(aq, const char *);
110 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
111 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
112 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
113 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
114 ast_odbc_release_obj(obj);
118 /* Now bind the parameters */
121 while((newparam = va_arg(ap, const char *))) {
122 newval = va_arg(ap, const char *);
123 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
126 res = ast_odbc_smart_execute(obj, stmt);
128 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
129 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
130 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
131 ast_odbc_release_obj(obj);
135 res = SQLNumResultCols(stmt, &colcount);
136 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
137 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
138 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
139 ast_odbc_release_obj(obj);
143 res = SQLFetch(stmt);
144 if (res == SQL_NO_DATA) {
145 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
146 ast_odbc_release_obj(obj);
149 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
150 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
151 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
152 ast_odbc_release_obj(obj);
155 for (x = 0; x < colcount; x++) {
157 collen = sizeof(coltitle);
158 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
159 &datatype, &colsize, &decimaldigits, &nullable);
160 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
161 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
163 ast_variables_destroy(var);
164 ast_odbc_release_obj(obj);
169 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
170 if (indicator == SQL_NULL_DATA)
173 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
174 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
176 ast_variables_destroy(var);
177 ast_odbc_release_obj(obj);
182 chunk = strsep(&stringp, ";");
183 if (!ast_strlen_zero(ast_strip(chunk))) {
185 prev->next = ast_variable_new(coltitle, chunk);
189 prev = var = ast_variable_new(coltitle, chunk);
196 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
197 ast_odbc_release_obj(obj);
201 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
203 struct odbc_obj *obj;
208 const char *initfield=NULL;
210 const char *newparam, *newval;
216 struct ast_variable *var=NULL;
217 struct ast_config *cfg=NULL;
218 struct ast_category *cat=NULL;
219 struct ast_realloca ra;
221 SQLSMALLINT colcount=0;
222 SQLSMALLINT datatype;
223 SQLSMALLINT decimaldigits;
224 SQLSMALLINT nullable;
225 SQLINTEGER indicator;
233 memset(&ra, 0, sizeof(ra));
235 obj = ast_odbc_request_obj(database, 0);
239 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
240 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
241 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
242 ast_odbc_release_obj(obj);
246 newparam = va_arg(aq, const char *);
248 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
249 ast_odbc_release_obj(obj);
252 initfield = ast_strdupa(newparam);
253 if ((op = strchr(initfield, ' ')))
255 newval = va_arg(aq, const char *);
256 if (!strchr(newparam, ' ')) op = " ="; else op = "";
257 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?", table, newparam, op);
258 while((newparam = va_arg(aq, const char *))) {
259 if (!strchr(newparam, ' ')) op = " ="; else op = "";
260 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?", newparam, op);
261 newval = va_arg(aq, const char *);
264 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
266 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
267 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
268 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
269 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
270 ast_odbc_release_obj(obj);
274 /* Now bind the parameters */
277 while((newparam = va_arg(ap, const char *))) {
278 newval = va_arg(ap, const char *);
279 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
282 res = ast_odbc_smart_execute(obj, stmt);
284 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
285 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
286 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
287 ast_odbc_release_obj(obj);
291 res = SQLNumResultCols(stmt, &colcount);
292 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
293 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
294 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
295 ast_odbc_release_obj(obj);
299 cfg = ast_config_new();
301 ast_log(LOG_WARNING, "Out of memory!\n");
302 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
303 ast_odbc_release_obj(obj);
307 while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
309 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
310 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
313 cat = ast_category_new("");
315 ast_log(LOG_WARNING, "Out of memory!\n");
318 for (x=0;x<colcount;x++) {
320 collen = sizeof(coltitle);
321 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
322 &datatype, &colsize, &decimaldigits, &nullable);
323 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
324 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
325 ast_category_destroy(cat);
330 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
331 if (indicator == SQL_NULL_DATA)
334 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
335 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
336 ast_category_destroy(cat);
341 chunk = strsep(&stringp, ";");
342 if (!ast_strlen_zero(ast_strip(chunk))) {
343 if (initfield && !strcmp(initfield, coltitle))
344 ast_category_rename(cat, chunk);
345 var = ast_variable_new(coltitle, chunk);
346 ast_variable_append(cat, var);
350 ast_category_append(cfg, cat);
353 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
354 ast_odbc_release_obj(obj);
358 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
360 struct odbc_obj *obj;
364 const char *newparam, *newval;
374 obj = ast_odbc_request_obj(database, 0);
378 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
379 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
380 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
381 ast_odbc_release_obj(obj);
385 newparam = va_arg(aq, const char *);
387 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
388 ast_odbc_release_obj(obj);
391 newval = va_arg(aq, const char *);
392 snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
393 while((newparam = va_arg(aq, const char *))) {
394 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
395 newval = va_arg(aq, const char *);
398 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
400 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
401 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
402 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
403 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
404 ast_odbc_release_obj(obj);
408 /* Now bind the parameters */
411 while((newparam = va_arg(ap, const char *))) {
412 newval = va_arg(ap, const char *);
413 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
416 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(lookup), 0, (void *)lookup, 0, NULL);
418 res = ast_odbc_smart_execute(obj, stmt);
420 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
421 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
422 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
423 ast_odbc_release_obj(obj);
427 res = SQLRowCount(stmt, &rowcount);
428 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
429 ast_odbc_release_obj(obj);
431 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
432 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
437 return (int)rowcount;
442 struct config_odbc_obj {
445 unsigned long cat_metric;
446 unsigned long var_metric;
447 unsigned long commented;
451 char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
455 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
457 struct config_odbc_obj *q = data;
461 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
462 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
463 if (option_verbose > 3)
464 ast_verbose( VERBOSE_PREFIX_4 "Failure in AllocStatement %d\n", res);
468 res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
469 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
470 if (option_verbose > 3)
471 ast_verbose( VERBOSE_PREFIX_4 "Error in PREPARE %d\n", res);
472 SQLFreeHandle(SQL_HANDLE_STMT, sth);
476 SQLBindCol(sth, 1, SQL_C_ULONG, &q->id, sizeof(q->id), &q->err);
477 SQLBindCol(sth, 2, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
478 SQLBindCol(sth, 3, SQL_C_ULONG, &q->var_metric, sizeof(q->var_metric), &q->err);
479 SQLBindCol(sth, 4, SQL_C_ULONG, &q->commented, sizeof(q->commented), &q->err);
480 SQLBindCol(sth, 5, SQL_C_CHAR, q->filename, sizeof(q->filename), &q->err);
481 SQLBindCol(sth, 6, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
482 SQLBindCol(sth, 7, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
483 SQLBindCol(sth, 8, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
488 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments)
490 struct ast_variable *new_v;
491 struct ast_category *cur_cat;
493 struct odbc_obj *obj;
495 unsigned int last_cat_metric = 0;
496 SQLSMALLINT rowcount=0;
499 struct config_odbc_obj q;
501 memset(&q, 0, sizeof(q));
503 if (!file || !strcmp (file, "res_config_odbc.conf"))
504 return NULL; /* cant configure myself with myself ! */
506 obj = ast_odbc_request_obj(database, 0);
510 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE filename='%s' and commented=0 ORDER BY filename,cat_metric desc,var_metric asc,category,var_name,var_val,id", table, file);
513 stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
516 ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
517 ast_odbc_release_obj(obj);
521 res = SQLNumResultCols(stmt, &rowcount);
523 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
524 ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
525 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
526 ast_odbc_release_obj(obj);
531 ast_log(LOG_NOTICE, "found nothing\n");
532 ast_odbc_release_obj(obj);
536 cur_cat = ast_config_get_current_category(cfg);
538 while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
539 if (!strcmp (q.var_name, "#include")) {
540 if (!ast_config_internal_load(q.var_val, cfg, 0)) {
541 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
542 ast_odbc_release_obj(obj);
547 if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
548 cur_cat = ast_category_new(q.category);
550 ast_log(LOG_WARNING, "Out of memory!\n");
553 strcpy(last, q.category);
554 last_cat_metric = q.cat_metric;
555 ast_category_append(cfg, cur_cat);
558 new_v = ast_variable_new(q.var_name, q.var_val);
559 ast_variable_append(cur_cat, new_v);
562 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
563 ast_odbc_release_obj(obj);
567 static struct ast_config_engine odbc_engine = {
569 .load_func = config_odbc,
570 .realtime_func = realtime_odbc,
571 .realtime_multi_func = realtime_multi_odbc,
572 .update_func = update_odbc
575 static int unload_module (void)
577 ast_module_user_hangup_all();
578 ast_config_engine_deregister(&odbc_engine);
580 ast_verbose("res_config_odbc unloaded.\n");
584 static int load_module (void)
586 ast_config_engine_register(&odbc_engine);
588 ast_verbose("res_config_odbc loaded.\n");
592 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "ODBC Configuration",
594 .unload = unload_module,