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