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>
34 <depend>res_odbc</depend>
39 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
46 #include "asterisk/file.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/config.h"
51 #include "asterisk/module.h"
52 #include "asterisk/lock.h"
53 #include "asterisk/options.h"
54 #include "asterisk/res_odbc.h"
55 #include "asterisk/utils.h"
57 struct custom_prepare_struct
{
63 static SQLHSTMT
custom_prepare(struct odbc_obj
*obj
, void *data
)
66 struct custom_prepare_struct
*cps
= data
;
67 const char *newparam
, *newval
;
73 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
74 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
75 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
79 res
= SQLPrepare(stmt
, (unsigned char *)cps
->sql
, SQL_NTS
);
80 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
81 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", cps
->sql
);
82 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
86 while ((newparam
= va_arg(ap
, const char *))) {
87 newval
= va_arg(ap
, const char *);
88 SQLBindParameter(stmt
, x
++, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(newval
), 0, (void *)newval
, 0, NULL
);
92 if (!ast_strlen_zero(cps
->extra
))
93 SQLBindParameter(stmt
, x
++, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(cps
->extra
), 0, (void *)cps
->extra
, 0, NULL
);
97 static struct ast_variable
*realtime_odbc(const char *database
, const char *table
, va_list ap
)
105 const char *newparam
, *newval
;
111 struct ast_variable
*var
=NULL
, *prev
=NULL
;
113 SQLSMALLINT colcount
=0;
114 SQLSMALLINT datatype
;
115 SQLSMALLINT decimaldigits
;
116 SQLSMALLINT nullable
;
119 struct custom_prepare_struct cps
= { .sql
= sql
};
127 obj
= ast_odbc_request_obj(database
, 0);
130 ast_log(LOG_ERROR
, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database
);
134 newparam
= va_arg(aq
, const char *);
136 ast_odbc_release_obj(obj
);
139 newval
= va_arg(aq
, const char *);
140 op
= !strchr(newparam
, ' ') ? " =" : "";
141 snprintf(sql
, sizeof(sql
), "SELECT * FROM %s WHERE %s%s ?%s", table
, newparam
, op
,
142 strcasestr(newparam
, "LIKE") && !ast_odbc_backslash_is_escape(obj
) ? " ESCAPE '\\'" : "");
143 while((newparam
= va_arg(aq
, const char *))) {
144 op
= !strchr(newparam
, ' ') ? " =" : "";
145 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), " AND %s%s ?%s", newparam
, op
,
146 strcasestr(newparam
, "LIKE") && !ast_odbc_backslash_is_escape(obj
) ? " ESCAPE '\\'" : "");
147 newval
= va_arg(aq
, const char *);
151 stmt
= ast_odbc_prepare_and_execute(obj
, custom_prepare
, &cps
);
154 ast_odbc_release_obj(obj
);
158 res
= SQLNumResultCols(stmt
, &colcount
);
159 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
160 ast_log(LOG_WARNING
, "SQL Column Count error!\n[%s]\n\n", sql
);
161 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
162 ast_odbc_release_obj(obj
);
166 res
= SQLFetch(stmt
);
167 if (res
== SQL_NO_DATA
) {
168 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
169 ast_odbc_release_obj(obj
);
172 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
173 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
174 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
175 ast_odbc_release_obj(obj
);
178 for (x
= 0; x
< colcount
; x
++) {
180 collen
= sizeof(coltitle
);
181 res
= SQLDescribeCol(stmt
, x
+ 1, (unsigned char *)coltitle
, sizeof(coltitle
), &collen
,
182 &datatype
, &colsize
, &decimaldigits
, &nullable
);
183 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
184 ast_log(LOG_WARNING
, "SQL Describe Column error!\n[%s]\n\n", sql
);
186 ast_variables_destroy(var
);
187 ast_odbc_release_obj(obj
);
192 res
= SQLGetData(stmt
, x
+ 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), &indicator
);
193 if (indicator
== SQL_NULL_DATA
)
196 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
197 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
199 ast_variables_destroy(var
);
200 ast_odbc_release_obj(obj
);
205 chunk
= strsep(&stringp
, ";");
206 if (!ast_strlen_zero(ast_strip(chunk
))) {
208 prev
->next
= ast_variable_new(coltitle
, chunk
);
212 prev
= var
= ast_variable_new(coltitle
, chunk
);
218 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
219 ast_odbc_release_obj(obj
);
223 static struct ast_config
*realtime_multi_odbc(const char *database
, const char *table
, va_list ap
)
225 struct odbc_obj
*obj
;
230 const char *initfield
=NULL
;
232 const char *newparam
, *newval
;
238 struct ast_variable
*var
=NULL
;
239 struct ast_config
*cfg
=NULL
;
240 struct ast_category
*cat
=NULL
;
241 struct ast_realloca ra
;
243 SQLSMALLINT colcount
=0;
244 SQLSMALLINT datatype
;
245 SQLSMALLINT decimaldigits
;
246 SQLSMALLINT nullable
;
248 struct custom_prepare_struct cps
= { .sql
= sql
};
256 memset(&ra
, 0, sizeof(ra
));
258 obj
= ast_odbc_request_obj(database
, 0);
262 newparam
= va_arg(aq
, const char *);
264 ast_odbc_release_obj(obj
);
267 initfield
= ast_strdupa(newparam
);
268 if ((op
= strchr(initfield
, ' ')))
270 newval
= va_arg(aq
, const char *);
271 op
= !strchr(newparam
, ' ') ? " =" : "";
272 snprintf(sql
, sizeof(sql
), "SELECT * FROM %s WHERE %s%s ?%s", table
, newparam
, op
,
273 strcasestr(newparam
, "LIKE") && !ast_odbc_backslash_is_escape(obj
) ? " ESCAPE '\\'" : "");
274 while((newparam
= va_arg(aq
, const char *))) {
275 op
= !strchr(newparam
, ' ') ? " =" : "";
276 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), " AND %s%s ?%s", newparam
, op
,
277 strcasestr(newparam
, "LIKE") && !ast_odbc_backslash_is_escape(obj
) ? " ESCAPE '\\'" : "");
278 newval
= va_arg(aq
, const char *);
281 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), " ORDER BY %s", initfield
);
284 stmt
= ast_odbc_prepare_and_execute(obj
, custom_prepare
, &cps
);
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
;
367 struct custom_prepare_struct cps
= { .sql
= sql
, .extra
= lookup
};
375 obj
= ast_odbc_request_obj(database
, 0);
379 newparam
= va_arg(aq
, const char *);
381 ast_odbc_release_obj(obj
);
384 newval
= va_arg(aq
, const char *);
385 snprintf(sql
, sizeof(sql
), "UPDATE %s SET %s=?", table
, newparam
);
386 while((newparam
= va_arg(aq
, const char *))) {
387 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), ", %s=?", newparam
);
388 newval
= va_arg(aq
, const char *);
391 snprintf(sql
+ strlen(sql
), sizeof(sql
) - strlen(sql
), " WHERE %s=?", keyfield
);
393 stmt
= ast_odbc_prepare_and_execute(obj
, custom_prepare
, &cps
);
396 ast_odbc_release_obj(obj
);
400 res
= SQLRowCount(stmt
, &rowcount
);
401 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
402 ast_odbc_release_obj(obj
);
404 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
405 ast_log(LOG_WARNING
, "SQL Row Count error!\n[%s]\n\n", sql
);
410 return (int)rowcount
;
415 struct config_odbc_obj
{
417 unsigned long cat_metric
;
420 char var_val
[1024]; /* changed from 128 to 1024 via bug 8251 */
424 static SQLHSTMT
config_odbc_prepare(struct odbc_obj
*obj
, void *data
)
426 struct config_odbc_obj
*q
= data
;
430 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &sth
);
431 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
432 if (option_verbose
> 3)
433 ast_verbose( VERBOSE_PREFIX_4
"Failure in AllocStatement %d\n", res
);
437 res
= SQLPrepare(sth
, (unsigned char *)q
->sql
, SQL_NTS
);
438 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
439 if (option_verbose
> 3)
440 ast_verbose( VERBOSE_PREFIX_4
"Error in PREPARE %d\n", res
);
441 SQLFreeHandle(SQL_HANDLE_STMT
, sth
);
445 SQLBindCol(sth
, 1, SQL_C_ULONG
, &q
->cat_metric
, sizeof(q
->cat_metric
), &q
->err
);
446 SQLBindCol(sth
, 2, SQL_C_CHAR
, q
->category
, sizeof(q
->category
), &q
->err
);
447 SQLBindCol(sth
, 3, SQL_C_CHAR
, q
->var_name
, sizeof(q
->var_name
), &q
->err
);
448 SQLBindCol(sth
, 4, SQL_C_CHAR
, q
->var_val
, sizeof(q
->var_val
), &q
->err
);
453 static struct ast_config
*config_odbc(const char *database
, const char *table
, const char *file
, struct ast_config
*cfg
, int withcomments
)
455 struct ast_variable
*new_v
;
456 struct ast_category
*cur_cat
;
458 struct odbc_obj
*obj
;
459 char sqlbuf
[1024] = "";
461 size_t sqlleft
= sizeof(sqlbuf
);
462 unsigned int last_cat_metric
= 0;
463 SQLSMALLINT rowcount
= 0;
466 struct config_odbc_obj q
;
468 memset(&q
, 0, sizeof(q
));
470 if (!file
|| !strcmp (file
, "res_config_odbc.conf"))
471 return NULL
; /* cant configure myself with myself ! */
473 obj
= ast_odbc_request_obj(database
, 0);
477 ast_build_string(&sql
, &sqlleft
, "SELECT cat_metric, category, var_name, var_val FROM %s ", table
);
478 ast_build_string(&sql
, &sqlleft
, "WHERE filename='%s' AND commented=0 ", file
);
479 ast_build_string(&sql
, &sqlleft
, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
482 stmt
= ast_odbc_prepare_and_execute(obj
, config_odbc_prepare
, &q
);
485 ast_log(LOG_WARNING
, "SQL select error!\n[%s]\n\n", sql
);
486 ast_odbc_release_obj(obj
);
490 res
= SQLNumResultCols(stmt
, &rowcount
);
492 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
493 ast_log(LOG_WARNING
, "SQL NumResultCols error!\n[%s]\n\n", sql
);
494 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
495 ast_odbc_release_obj(obj
);
500 ast_log(LOG_NOTICE
, "found nothing\n");
501 ast_odbc_release_obj(obj
);
505 cur_cat
= ast_config_get_current_category(cfg
);
507 while ((res
= SQLFetch(stmt
)) != SQL_NO_DATA
) {
508 if (!strcmp (q
.var_name
, "#include")) {
509 if (!ast_config_internal_load(q
.var_val
, cfg
, 0)) {
510 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
511 ast_odbc_release_obj(obj
);
516 if (strcmp(last
, q
.category
) || last_cat_metric
!= q
.cat_metric
) {
517 cur_cat
= ast_category_new(q
.category
);
519 ast_log(LOG_WARNING
, "Out of memory!\n");
522 strcpy(last
, q
.category
);
523 last_cat_metric
= q
.cat_metric
;
524 ast_category_append(cfg
, cur_cat
);
527 new_v
= ast_variable_new(q
.var_name
, q
.var_val
);
528 ast_variable_append(cur_cat
, new_v
);
531 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
532 ast_odbc_release_obj(obj
);
536 static struct ast_config_engine odbc_engine
= {
538 .load_func
= config_odbc
,
539 .realtime_func
= realtime_odbc
,
540 .realtime_multi_func
= realtime_multi_odbc
,
541 .update_func
= update_odbc
544 static int unload_module (void)
546 ast_module_user_hangup_all();
547 ast_config_engine_deregister(&odbc_engine
);
549 ast_verbose("res_config_odbc unloaded.\n");
553 static int load_module (void)
555 ast_config_engine_register(&odbc_engine
);
557 ast_verbose("res_config_odbc loaded.\n");
561 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "ODBC Configuration",
563 .unload
= unload_module
,