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: 87262 $")
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 = 0, 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 {
00420 free(*query);
00421 *query = NULL;
00422 return -1;
00423 }
00424
00425 if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00426 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00427 }
00428
00429 if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00430 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00431 }
00432
00433
00434 ast_set_flag((*query), OPT_ESCAPECOMMAS);
00435 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00436 if (ast_false(tmp))
00437 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00438 }
00439
00440 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00441 if (! (*query)->acf) {
00442 free(*query);
00443 *query = NULL;
00444 return -1;
00445 }
00446
00447 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00448 asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg);
00449 } else {
00450 asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg);
00451 }
00452
00453 if (!((*query)->acf->name)) {
00454 free((*query)->acf);
00455 free(*query);
00456 *query = NULL;
00457 return -1;
00458 }
00459
00460 asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
00461
00462 if (!((*query)->acf->syntax)) {
00463 free((char *)(*query)->acf->name);
00464 free((*query)->acf);
00465 free(*query);
00466 *query = NULL;
00467 return -1;
00468 }
00469
00470 (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00471 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00472 asprintf((char **)&((*query)->acf->desc),
00473 "Runs the following query, as defined in func_odbc.conf, performing\n"
00474 "substitution of the arguments into the query as specified by ${ARG1},\n"
00475 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
00476 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00477 "\nRead:\n%s\n\nWrite:\n%s\n",
00478 (*query)->sql_read,
00479 (*query)->sql_write);
00480 } else if (!ast_strlen_zero((*query)->sql_read)) {
00481 asprintf((char **)&((*query)->acf->desc),
00482 "Runs the following query, as defined in func_odbc.conf, performing\n"
00483 "substitution of the arguments into the query as specified by ${ARG1},\n"
00484 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
00485 (*query)->sql_read);
00486 } else if (!ast_strlen_zero((*query)->sql_write)) {
00487 asprintf((char **)&((*query)->acf->desc),
00488 "Runs the following query, as defined in func_odbc.conf, performing\n"
00489 "substitution of the arguments into the query as specified by ${ARG1},\n"
00490 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
00491 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00492 "This function may only be set.\nSQL:\n%s\n",
00493 (*query)->sql_write);
00494 }
00495
00496
00497 if (! ((*query)->acf->desc)) {
00498 free((char *)(*query)->acf->syntax);
00499 free((char *)(*query)->acf->name);
00500 free((*query)->acf);
00501 free(*query);
00502 *query = NULL;
00503 return -1;
00504 }
00505
00506 if (ast_strlen_zero((*query)->sql_read)) {
00507 (*query)->acf->read = NULL;
00508 } else {
00509 (*query)->acf->read = acf_odbc_read;
00510 }
00511
00512 if (ast_strlen_zero((*query)->sql_write)) {
00513 (*query)->acf->write = NULL;
00514 } else {
00515 (*query)->acf->write = acf_odbc_write;
00516 }
00517
00518 return 0;
00519 }
00520
00521 static int free_acf_query(struct acf_odbc_query *query)
00522 {
00523 if (query) {
00524 if (query->acf) {
00525 if (query->acf->name)
00526 free((char *)query->acf->name);
00527 if (query->acf->syntax)
00528 free((char *)query->acf->syntax);
00529 if (query->acf->desc)
00530 free((char *)query->acf->desc);
00531 free(query->acf);
00532 }
00533 free(query);
00534 }
00535 return 0;
00536 }
00537
00538 static int odbc_load_module(void)
00539 {
00540 int res = 0;
00541 struct ast_config *cfg;
00542 char *catg;
00543
00544 AST_LIST_LOCK(&queries);
00545
00546 cfg = ast_config_load(config);
00547 if (!cfg) {
00548 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
00549 AST_LIST_UNLOCK(&queries);
00550 return AST_MODULE_LOAD_DECLINE;
00551 }
00552
00553 for (catg = ast_category_browse(cfg, NULL);
00554 catg;
00555 catg = ast_category_browse(cfg, catg)) {
00556 struct acf_odbc_query *query = NULL;
00557
00558 if (init_acf_query(cfg, catg, &query)) {
00559 free_acf_query(query);
00560 } else {
00561 AST_LIST_INSERT_HEAD(&queries, query, list);
00562 ast_custom_function_register(query->acf);
00563 }
00564 }
00565
00566 ast_config_destroy(cfg);
00567 ast_custom_function_register(&escape_function);
00568
00569 AST_LIST_UNLOCK(&queries);
00570 return res;
00571 }
00572
00573 static int odbc_unload_module(void)
00574 {
00575 struct acf_odbc_query *query;
00576
00577 AST_LIST_LOCK(&queries);
00578 while (!AST_LIST_EMPTY(&queries)) {
00579 query = AST_LIST_REMOVE_HEAD(&queries, list);
00580 ast_custom_function_unregister(query->acf);
00581 free_acf_query(query);
00582 }
00583
00584 ast_custom_function_unregister(&escape_function);
00585
00586
00587 AST_LIST_UNLOCK(&queries);
00588 AST_LIST_LOCK(&queries);
00589
00590 AST_LIST_UNLOCK(&queries);
00591 return 0;
00592 }
00593
00594 static int reload(void)
00595 {
00596 int res = 0;
00597 struct ast_config *cfg;
00598 struct acf_odbc_query *oldquery;
00599 char *catg;
00600
00601 AST_LIST_LOCK(&queries);
00602
00603 while (!AST_LIST_EMPTY(&queries)) {
00604 oldquery = AST_LIST_REMOVE_HEAD(&queries, list);
00605 ast_custom_function_unregister(oldquery->acf);
00606 free_acf_query(oldquery);
00607 }
00608
00609 cfg = ast_config_load(config);
00610 if (!cfg) {
00611 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00612 goto reload_out;
00613 }
00614
00615 for (catg = ast_category_browse(cfg, NULL);
00616 catg;
00617 catg = ast_category_browse(cfg, catg)) {
00618 struct acf_odbc_query *query = NULL;
00619
00620 if (init_acf_query(cfg, catg, &query)) {
00621 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00622 } else {
00623 AST_LIST_INSERT_HEAD(&queries, query, list);
00624 ast_custom_function_register(query->acf);
00625 }
00626 }
00627
00628 ast_config_destroy(cfg);
00629 reload_out:
00630 AST_LIST_UNLOCK(&queries);
00631 return res;
00632 }
00633
00634 static int unload_module(void)
00635 {
00636 return odbc_unload_module();
00637 }
00638
00639 static int load_module(void)
00640 {
00641 return odbc_load_module();
00642 }
00643
00644
00645
00646 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
00647 .load = load_module,
00648 .unload = unload_module,
00649 .reload = reload,
00650 );
00651