00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "asterisk.h"
00034
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 146799 $")
00036
00037 #include <sys/types.h>
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <unistd.h>
00041 #include <string.h>
00042
00043 #include "asterisk/module.h"
00044 #include "asterisk/file.h"
00045 #include "asterisk/logger.h"
00046 #include "asterisk/options.h"
00047 #include "asterisk/channel.h"
00048 #include "asterisk/pbx.h"
00049 #include "asterisk/module.h"
00050 #include "asterisk/config.h"
00051 #include "asterisk/res_odbc.h"
00052 #include "asterisk/app.h"
00053
00054 static char *config = "func_odbc.conf";
00055
00056 enum {
00057 OPT_ESCAPECOMMAS = (1 << 0),
00058 } odbc_option_flags;
00059
00060 struct acf_odbc_query {
00061 AST_LIST_ENTRY(acf_odbc_query) list;
00062 char dsn[30];
00063 char sql_read[2048];
00064 char sql_write[2048];
00065 unsigned int flags;
00066 struct ast_custom_function *acf;
00067 };
00068
00069 AST_LIST_HEAD_STATIC(queries, acf_odbc_query);
00070
00071 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
00072 {
00073 int res;
00074 char *sql = data;
00075 SQLHSTMT stmt;
00076
00077 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00078 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00079 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00080 return NULL;
00081 }
00082
00083 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
00084 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00085 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
00086 SQLCloseCursor(stmt);
00087 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00088 return NULL;
00089 }
00090
00091 return stmt;
00092 }
00093
00094
00095
00096
00097 static int acf_odbc_write(struct ast_channel *chan, char *cmd, char *s, const char *value)
00098 {
00099 struct odbc_obj *obj;
00100 struct acf_odbc_query *query;
00101 char *t, buf[2048]="", varname[15];
00102 int i, bogus_chan = 0;
00103 AST_DECLARE_APP_ARGS(values,
00104 AST_APP_ARG(field)[100];
00105 );
00106 AST_DECLARE_APP_ARGS(args,
00107 AST_APP_ARG(field)[100];
00108 );
00109 SQLHSTMT stmt;
00110 SQLLEN rows=0;
00111
00112 AST_LIST_LOCK(&queries);
00113 AST_LIST_TRAVERSE(&queries, query, list) {
00114 if (!strcmp(query->acf->name, cmd)) {
00115 break;
00116 }
00117 }
00118
00119 if (!query) {
00120 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00121 AST_LIST_UNLOCK(&queries);
00122 return -1;
00123 }
00124
00125 obj = ast_odbc_request_obj(query->dsn, 0);
00126
00127 if (!obj) {
00128 ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", query->dsn);
00129 AST_LIST_UNLOCK(&queries);
00130 return -1;
00131 }
00132
00133 if (!chan) {
00134 if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00135 bogus_chan = 1;
00136 }
00137
00138 if (chan)
00139 ast_autoservice_start(chan);
00140
00141
00142 t = value ? ast_strdupa(value) : "";
00143
00144 if (!s || !t) {
00145 ast_log(LOG_ERROR, "Out of memory\n");
00146 AST_LIST_UNLOCK(&queries);
00147 if (chan)
00148 ast_autoservice_stop(chan);
00149 if (bogus_chan)
00150 ast_channel_free(chan);
00151 return -1;
00152 }
00153
00154 AST_STANDARD_APP_ARGS(args, s);
00155 for (i = 0; i < args.argc; i++) {
00156 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00157 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00158 }
00159
00160
00161
00162 AST_NONSTANDARD_APP_ARGS(values, t, ',');
00163 for (i = 0; i < values.argc; i++) {
00164 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00165 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00166 }
00167
00168
00169 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00170
00171 pbx_substitute_variables_helper(chan, query->sql_write, buf, sizeof(buf) - 1);
00172
00173
00174 for (i = 0; i < args.argc; i++) {
00175 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00176 pbx_builtin_setvar_helper(chan, varname, NULL);
00177 }
00178
00179 for (i = 0; i < values.argc; i++) {
00180 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00181 pbx_builtin_setvar_helper(chan, varname, NULL);
00182 }
00183 pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00184
00185 AST_LIST_UNLOCK(&queries);
00186
00187 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, buf);
00188
00189 if (stmt) {
00190
00191 SQLRowCount(stmt, &rows);
00192 }
00193
00194
00195
00196
00197
00198 snprintf(varname, sizeof(varname), "%d", (int)rows);
00199 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00200
00201 if (stmt) {
00202 SQLCloseCursor(stmt);
00203 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00204 }
00205 if (obj)
00206 ast_odbc_release_obj(obj);
00207
00208 if (chan)
00209 ast_autoservice_stop(chan);
00210 if (bogus_chan)
00211 ast_channel_free(chan);
00212
00213 return 0;
00214 }
00215
00216 static int acf_odbc_read(struct ast_channel *chan, char *cmd, char *s, char *buf, size_t len)
00217 {
00218 struct odbc_obj *obj;
00219 struct acf_odbc_query *query;
00220 char sql[2048] = "", varname[15];
00221 int res, x, buflen = 1, escapecommas, bogus_chan = 0;
00222 AST_DECLARE_APP_ARGS(args,
00223 AST_APP_ARG(field)[100];
00224 );
00225 SQLHSTMT stmt;
00226 SQLSMALLINT colcount=0;
00227 SQLLEN indicator;
00228
00229 AST_LIST_LOCK(&queries);
00230 AST_LIST_TRAVERSE(&queries, query, list) {
00231 if (!strcmp(query->acf->name, cmd)) {
00232 break;
00233 }
00234 }
00235
00236 if (!query) {
00237 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00238 AST_LIST_UNLOCK(&queries);
00239 return -1;
00240 }
00241
00242 obj = ast_odbc_request_obj(query->dsn, 0);
00243
00244 if (!obj) {
00245 ast_log(LOG_ERROR, "No such DSN registered (or out of connections): %s (check res_odbc.conf)\n", query->dsn);
00246 AST_LIST_UNLOCK(&queries);
00247 return -1;
00248 }
00249
00250 if (!chan) {
00251 if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00252 bogus_chan = 1;
00253 }
00254
00255 if (chan)
00256 ast_autoservice_start(chan);
00257
00258 AST_STANDARD_APP_ARGS(args, s);
00259 for (x = 0; x < args.argc; x++) {
00260 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00261 pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00262 }
00263
00264 pbx_substitute_variables_helper(chan, query->sql_read, sql, sizeof(sql) - 1);
00265
00266
00267 for (x = 0; x < args.argc; x++) {
00268 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00269 pbx_builtin_setvar_helper(chan, varname, NULL);
00270 }
00271
00272
00273 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00274
00275 AST_LIST_UNLOCK(&queries);
00276
00277 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, sql);
00278
00279 if (!stmt) {
00280 ast_odbc_release_obj(obj);
00281 if (chan)
00282 ast_autoservice_stop(chan);
00283 if (bogus_chan)
00284 ast_channel_free(chan);
00285 return -1;
00286 }
00287
00288 res = SQLNumResultCols(stmt, &colcount);
00289 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00290 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00291 SQLCloseCursor(stmt);
00292 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00293 ast_odbc_release_obj(obj);
00294 if (chan)
00295 ast_autoservice_stop(chan);
00296 if (bogus_chan)
00297 ast_channel_free(chan);
00298 return -1;
00299 }
00300
00301 *buf = '\0';
00302
00303 res = SQLFetch(stmt);
00304 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00305 int res1 = -1;
00306 if (res == SQL_NO_DATA) {
00307 if (option_verbose > 3) {
00308 ast_verbose(VERBOSE_PREFIX_4 "Found no rows [%s]\n", sql);
00309 }
00310 res1 = 0;
00311 } else if (option_verbose > 3) {
00312 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql);
00313 }
00314 SQLCloseCursor(stmt);
00315 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00316 ast_odbc_release_obj(obj);
00317 if (chan)
00318 ast_autoservice_stop(chan);
00319 if (bogus_chan)
00320 ast_channel_free(chan);
00321 return res1;
00322 }
00323
00324 for (x = 0; x < colcount; x++) {
00325 int i;
00326 char coldata[256];
00327
00328 buflen = strlen(buf);
00329 res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator);
00330 if (indicator == SQL_NULL_DATA) {
00331 coldata[0] = '\0';
00332 res = SQL_SUCCESS;
00333 }
00334
00335 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00336 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00337 SQLCloseCursor(stmt);
00338 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00339 ast_odbc_release_obj(obj);
00340 if (chan)
00341 ast_autoservice_stop(chan);
00342 if (bogus_chan)
00343 ast_channel_free(chan);
00344 return -1;
00345 }
00346
00347
00348 for (i = 0; i < sizeof(coldata); i++) {
00349 if (escapecommas && (coldata[i] == '\\' || coldata[i] == ',')) {
00350 buf[buflen++] = '\\';
00351 }
00352 buf[buflen++] = coldata[i];
00353
00354 if (buflen >= len - 2)
00355 break;
00356
00357 if (coldata[i] == '\0')
00358 break;
00359 }
00360
00361 buf[buflen - 1] = ',';
00362 buf[buflen] = '\0';
00363 }
00364
00365 buf[buflen - 1] = '\0';
00366
00367 SQLCloseCursor(stmt);
00368 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00369 ast_odbc_release_obj(obj);
00370 if (chan)
00371 ast_autoservice_stop(chan);
00372 if (bogus_chan)
00373 ast_channel_free(chan);
00374 return 0;
00375 }
00376
00377 static int acf_escape(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
00378 {
00379 char *out = buf;
00380
00381 for (; *data && out - buf < len; data++) {
00382 if (*data == '\'') {
00383 *out = '\'';
00384 out++;
00385 }
00386 *out++ = *data;
00387 }
00388 *out = '\0';
00389
00390 return 0;
00391 }
00392
00393 static struct ast_custom_function escape_function = {
00394 .name = "SQL_ESC",
00395 .synopsis = "Escapes single ticks for use in SQL statements",
00396 .syntax = "SQL_ESC(<string>)",
00397 .desc =
00398 "Used in SQL templates to escape data which may contain single ticks (') which\n"
00399 "are otherwise used to delimit data. For example:\n"
00400 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
00401 .read = acf_escape,
00402 .write = NULL,
00403 };
00404
00405 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00406 {
00407 const char *tmp;
00408
00409 if (!cfg || !catg) {
00410 return -1;
00411 }
00412
00413 *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00414 if (! (*query))
00415 return -1;
00416
00417 if ((tmp = ast_variable_retrieve(cfg, catg, "dsn"))) {
00418 ast_copy_string((*query)->dsn, tmp, sizeof((*query)->dsn));
00419 } else if ((tmp = ast_variable_retrieve(cfg, catg, "writehandle")) || (tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00420 ast_log(LOG_WARNING, "Separate read and write handles are not supported in this version of func_odbc.so\n");
00421 ast_copy_string((*query)->dsn, tmp, sizeof((*query)->dsn));
00422 } else {
00423 free(*query);
00424 *query = NULL;
00425 ast_log(LOG_ERROR, "No database handle was specified for func_odbc class '%s'\n", catg);
00426 return -1;
00427 }
00428
00429 if ((tmp = ast_variable_retrieve(cfg, catg, "read")) || (tmp = ast_variable_retrieve(cfg, catg, "readsql"))) {
00430 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00431 }
00432
00433 if ((tmp = ast_variable_retrieve(cfg, catg, "write")) || (tmp = ast_variable_retrieve(cfg, catg, "writesql"))) {
00434 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00435 }
00436
00437
00438 ast_set_flag((*query), OPT_ESCAPECOMMAS);
00439 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00440 if (ast_false(tmp))
00441 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00442 }
00443
00444 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00445 if (! (*query)->acf) {
00446 free(*query);
00447 *query = NULL;
00448 return -1;
00449 }
00450
00451 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00452 asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
00453 } else {
00454 asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
00455 }
00456
00457 if (!((*query)->acf->name)) {
00458 free((*query)->acf);
00459 free(*query);
00460 *query = NULL;
00461 return -1;
00462 }
00463
00464 asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
00465
00466 if (!((*query)->acf->syntax)) {
00467 free((char *)(*query)->acf->name);
00468 free((*query)->acf);
00469 free(*query);
00470 *query = NULL;
00471 return -1;
00472 }
00473
00474 (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00475 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00476 asprintf((char **)&((*query)->acf->desc),
00477 "Runs the following query, as defined in func_odbc.conf, performing\n"
00478 "substitution of the arguments into the query as specified by ${ARG1},\n"
00479 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
00480 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00481 "\nRead:\n%s\n\nWrite:\n%s\n",
00482 (*query)->sql_read,
00483 (*query)->sql_write);
00484 } else if (!ast_strlen_zero((*query)->sql_read)) {
00485 asprintf((char **)&((*query)->acf->desc),
00486 "Runs the following query, as defined in func_odbc.conf, performing\n"
00487 "substitution of the arguments into the query as specified by ${ARG1},\n"
00488 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
00489 (*query)->sql_read);
00490 } else if (!ast_strlen_zero((*query)->sql_write)) {
00491 asprintf((char **)&((*query)->acf->desc),
00492 "Runs the following query, as defined in func_odbc.conf, performing\n"
00493 "substitution of the arguments into the query as specified by ${ARG1},\n"
00494 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
00495 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00496 "This function may only be set.\nSQL:\n%s\n",
00497 (*query)->sql_write);
00498 } else {
00499 ast_log(LOG_ERROR, "No SQL was found for func_odbc class '%s'\n", catg);
00500 }
00501
00502
00503 if (! ((*query)->acf->desc)) {
00504 free((char *)(*query)->acf->syntax);
00505 free((char *)(*query)->acf->name);
00506 free((*query)->acf);
00507 free(*query);
00508 *query = NULL;
00509 return -1;
00510 }
00511
00512 if (ast_strlen_zero((*query)->sql_read)) {
00513 (*query)->acf->read = NULL;
00514 } else {
00515 (*query)->acf->read = acf_odbc_read;
00516 }
00517
00518 if (ast_strlen_zero((*query)->sql_write)) {
00519 (*query)->acf->write = NULL;
00520 } else {
00521 (*query)->acf->write = acf_odbc_write;
00522 }
00523
00524 return 0;
00525 }
00526
00527 static int free_acf_query(struct acf_odbc_query *query)
00528 {
00529 if (query) {
00530 if (query->acf) {
00531 if (query->acf->name)
00532 free((char *)query->acf->name);
00533 if (query->acf->syntax)
00534 free((char *)query->acf->syntax);
00535 if (query->acf->desc)
00536 free((char *)query->acf->desc);
00537 free(query->acf);
00538 }
00539 free(query);
00540 }
00541 return 0;
00542 }
00543
00544 static int odbc_load_module(void)
00545 {
00546 int res = 0;
00547 struct ast_config *cfg;
00548 char *catg;
00549
00550 AST_LIST_LOCK(&queries);
00551
00552 cfg = ast_config_load(config);
00553 if (!cfg) {
00554 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
00555 AST_LIST_UNLOCK(&queries);
00556 return AST_MODULE_LOAD_DECLINE;
00557 }
00558
00559 for (catg = ast_category_browse(cfg, NULL);
00560 catg;
00561 catg = ast_category_browse(cfg, catg)) {
00562 struct acf_odbc_query *query = NULL;
00563
00564 if (init_acf_query(cfg, catg, &query)) {
00565 free_acf_query(query);
00566 } else {
00567 AST_LIST_INSERT_HEAD(&queries, query, list);
00568 ast_custom_function_register(query->acf);
00569 }
00570 }
00571
00572 ast_config_destroy(cfg);
00573 ast_custom_function_register(&escape_function);
00574
00575 AST_LIST_UNLOCK(&queries);
00576 return res;
00577 }
00578
00579 static int odbc_unload_module(void)
00580 {
00581 struct acf_odbc_query *query;
00582
00583 AST_LIST_LOCK(&queries);
00584 while (!AST_LIST_EMPTY(&queries)) {
00585 query = AST_LIST_REMOVE_HEAD(&queries, list);
00586 ast_custom_function_unregister(query->acf);
00587 free_acf_query(query);
00588 }
00589
00590 ast_custom_function_unregister(&escape_function);
00591
00592
00593 AST_LIST_UNLOCK(&queries);
00594 AST_LIST_LOCK(&queries);
00595
00596 AST_LIST_UNLOCK(&queries);
00597 return 0;
00598 }
00599
00600 static int reload(void)
00601 {
00602 int res = 0;
00603 struct ast_config *cfg;
00604 struct acf_odbc_query *oldquery;
00605 char *catg;
00606
00607 AST_LIST_LOCK(&queries);
00608
00609 while (!AST_LIST_EMPTY(&queries)) {
00610 oldquery = AST_LIST_REMOVE_HEAD(&queries, list);
00611 ast_custom_function_unregister(oldquery->acf);
00612 free_acf_query(oldquery);
00613 }
00614
00615 cfg = ast_config_load(config);
00616 if (!cfg) {
00617 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00618 goto reload_out;
00619 }
00620
00621 for (catg = ast_category_browse(cfg, NULL);
00622 catg;
00623 catg = ast_category_browse(cfg, catg)) {
00624 struct acf_odbc_query *query = NULL;
00625
00626 if (init_acf_query(cfg, catg, &query)) {
00627 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00628 } else {
00629 AST_LIST_INSERT_HEAD(&queries, query, list);
00630 ast_custom_function_register(query->acf);
00631 }
00632 }
00633
00634 ast_config_destroy(cfg);
00635 reload_out:
00636 AST_LIST_UNLOCK(&queries);
00637 return res;
00638 }
00639
00640 static int unload_module(void)
00641 {
00642 return odbc_unload_module();
00643 }
00644
00645 static int load_module(void)
00646 {
00647 return odbc_load_module();
00648 }
00649
00650
00651
00652 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
00653 .load = load_module,
00654 .unload = unload_module,
00655 .reload = reload,
00656 );
00657