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
00034
00035 #include "asterisk.h"
00036
00037 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 352291 $")
00038
00039 #include "asterisk/module.h"
00040 #include "asterisk/file.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/config.h"
00044 #include "asterisk/res_odbc.h"
00045 #include "asterisk/app.h"
00046 #include "asterisk/cli.h"
00047 #include "asterisk/strings.h"
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101 static char *config = "func_odbc.conf";
00102
00103 enum odbc_option_flags {
00104 OPT_ESCAPECOMMAS = (1 << 0),
00105 OPT_MULTIROW = (1 << 1),
00106 };
00107
00108 struct acf_odbc_query {
00109 AST_RWLIST_ENTRY(acf_odbc_query) list;
00110 char readhandle[5][30];
00111 char writehandle[5][30];
00112 char sql_read[2048];
00113 char sql_write[2048];
00114 char sql_insert[2048];
00115 unsigned int flags;
00116 int rowlimit;
00117 struct ast_custom_function *acf;
00118 };
00119
00120 static void odbc_datastore_free(void *data);
00121
00122 static struct ast_datastore_info odbc_info = {
00123 .type = "FUNC_ODBC",
00124 .destroy = odbc_datastore_free,
00125 };
00126
00127
00128 struct odbc_datastore_row {
00129 AST_LIST_ENTRY(odbc_datastore_row) list;
00130 char data[0];
00131 };
00132
00133
00134 struct odbc_datastore {
00135 AST_LIST_HEAD(, odbc_datastore_row);
00136 char names[0];
00137 };
00138
00139 static AST_RWLIST_HEAD_STATIC(queries, acf_odbc_query);
00140
00141 static int resultcount = 0;
00142
00143 AST_THREADSTORAGE(sql_buf);
00144 AST_THREADSTORAGE(sql2_buf);
00145 AST_THREADSTORAGE(coldata_buf);
00146 AST_THREADSTORAGE(colnames_buf);
00147
00148 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
00149
00150 static void odbc_datastore_free(void *data)
00151 {
00152 struct odbc_datastore *result = data;
00153 struct odbc_datastore_row *row;
00154 AST_LIST_LOCK(result);
00155 while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
00156 ast_free(row);
00157 }
00158 AST_LIST_UNLOCK(result);
00159 AST_LIST_HEAD_DESTROY(result);
00160 ast_free(result);
00161 }
00162
00163 static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
00164 {
00165 int res;
00166 char *sql = data;
00167 SQLHSTMT stmt;
00168
00169 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00170 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00171 ast_log(LOG_WARNING, "SQL Alloc Handle failed (%d)!\n", res);
00172 return NULL;
00173 }
00174
00175 res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
00176 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO) && (res != SQL_NO_DATA)) {
00177 if (res == SQL_ERROR) {
00178 int i;
00179 SQLINTEGER nativeerror=0, numfields=0;
00180 SQLSMALLINT diagbytes=0;
00181 unsigned char state[10], diagnostic[256];
00182
00183 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00184 for (i = 0; i < numfields; i++) {
00185 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00186 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00187 if (i > 10) {
00188 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00189 break;
00190 }
00191 }
00192 }
00193
00194 ast_log(LOG_WARNING, "SQL Exec Direct failed (%d)![%s]\n", res, sql);
00195 SQLCloseCursor(stmt);
00196 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00197 return NULL;
00198 }
00199
00200 return stmt;
00201 }
00202
00203
00204
00205
00206 static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
00207 {
00208 struct odbc_obj *obj = NULL;
00209 struct acf_odbc_query *query;
00210 char *t, varname[15];
00211 int i, dsn, bogus_chan = 0;
00212 int transactional = 0;
00213 AST_DECLARE_APP_ARGS(values,
00214 AST_APP_ARG(field)[100];
00215 );
00216 AST_DECLARE_APP_ARGS(args,
00217 AST_APP_ARG(field)[100];
00218 );
00219 SQLHSTMT stmt = NULL;
00220 SQLLEN rows=0;
00221 struct ast_str *buf = ast_str_thread_get(&sql_buf, 16);
00222 struct ast_str *insertbuf = ast_str_thread_get(&sql2_buf, 16);
00223 const char *status = "FAILURE";
00224
00225 if (!buf || !insertbuf) {
00226 return -1;
00227 }
00228
00229 AST_RWLIST_RDLOCK(&queries);
00230 AST_RWLIST_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_RWLIST_UNLOCK(&queries);
00239 if (chan) {
00240 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00241 }
00242 return -1;
00243 }
00244
00245 if (!chan) {
00246 if (!(chan = ast_dummy_channel_alloc())) {
00247 AST_RWLIST_UNLOCK(&queries);
00248 return -1;
00249 }
00250 bogus_chan = 1;
00251 }
00252
00253 if (!bogus_chan) {
00254 ast_autoservice_start(chan);
00255 }
00256
00257 ast_str_make_space(&buf, strlen(query->sql_write) * 2 + 300);
00258 ast_str_make_space(&insertbuf, strlen(query->sql_insert) * 2 + 300);
00259
00260
00261 t = value ? ast_strdupa(value) : "";
00262
00263 if (!s || !t) {
00264 ast_log(LOG_ERROR, "Out of memory\n");
00265 AST_RWLIST_UNLOCK(&queries);
00266 if (!bogus_chan) {
00267 ast_autoservice_stop(chan);
00268 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00269 } else {
00270 ast_channel_unref(chan);
00271 }
00272 return -1;
00273 }
00274
00275 AST_STANDARD_APP_ARGS(args, s);
00276 for (i = 0; i < args.argc; i++) {
00277 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00278 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00279 }
00280
00281
00282 AST_STANDARD_APP_ARGS(values, t);
00283 for (i = 0; i < values.argc; i++) {
00284 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00285 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00286 }
00287
00288
00289 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00290
00291 ast_str_substitute_variables(&buf, 0, chan, query->sql_write);
00292 ast_str_substitute_variables(&insertbuf, 0, chan, query->sql_insert);
00293
00294 if (bogus_chan) {
00295 chan = ast_channel_unref(chan);
00296 } else {
00297
00298 for (i = 0; i < args.argc; i++) {
00299 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00300 pbx_builtin_setvar_helper(chan, varname, NULL);
00301 }
00302
00303 for (i = 0; i < values.argc; i++) {
00304 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00305 pbx_builtin_setvar_helper(chan, varname, NULL);
00306 }
00307 pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00308 }
00309
00310
00311
00312
00313
00314
00315
00316 for (dsn = 0; dsn < 5; dsn++) {
00317 if (!ast_strlen_zero(query->writehandle[dsn])) {
00318 if (transactional) {
00319
00320 ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
00321 }
00322
00323 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
00324 transactional = 1;
00325 } else {
00326 obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00327 transactional = 0;
00328 }
00329
00330 if (obj && (stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(buf)))) {
00331 break;
00332 }
00333
00334 if (obj && !transactional) {
00335 ast_odbc_release_obj(obj);
00336 obj = NULL;
00337 }
00338 }
00339 }
00340
00341 if (stmt) {
00342 SQLRowCount(stmt, &rows);
00343 }
00344
00345 if (stmt && rows == 0 && ast_str_strlen(insertbuf) != 0) {
00346 SQLCloseCursor(stmt);
00347 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00348 if (obj && !transactional) {
00349 ast_odbc_release_obj(obj);
00350 obj = NULL;
00351 }
00352
00353 for (transactional = 0, dsn = 0; dsn < 5; dsn++) {
00354 if (!ast_strlen_zero(query->writehandle[dsn])) {
00355 if (transactional) {
00356
00357 ast_log(LOG_WARNING, "Transactions do not work well with multiple DSNs for 'writehandle'\n");
00358 } else if (obj) {
00359 ast_odbc_release_obj(obj);
00360 obj = NULL;
00361 }
00362
00363 if ((obj = ast_odbc_retrieve_transaction_obj(chan, query->writehandle[dsn]))) {
00364 transactional = 1;
00365 } else {
00366 obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00367 transactional = 0;
00368 }
00369 if (obj) {
00370 stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(insertbuf));
00371 }
00372 }
00373 if (stmt) {
00374 status = "FAILOVER";
00375 SQLRowCount(stmt, &rows);
00376 break;
00377 }
00378 }
00379 } else if (stmt) {
00380 status = "SUCCESS";
00381 }
00382
00383 AST_RWLIST_UNLOCK(&queries);
00384
00385
00386
00387
00388
00389 if (!bogus_chan) {
00390 snprintf(varname, sizeof(varname), "%d", (int)rows);
00391 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00392 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00393 }
00394
00395 if (stmt) {
00396 SQLCloseCursor(stmt);
00397 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00398 }
00399 if (obj && !transactional) {
00400 ast_odbc_release_obj(obj);
00401 obj = NULL;
00402 }
00403
00404 if (!bogus_chan) {
00405 ast_autoservice_stop(chan);
00406 }
00407
00408 return 0;
00409 }
00410
00411 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
00412 {
00413 struct odbc_obj *obj = NULL;
00414 struct acf_odbc_query *query;
00415 char varname[15], rowcount[12] = "-1";
00416 struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
00417 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, multirow = 0, dsn, bogus_chan = 0;
00418 AST_DECLARE_APP_ARGS(args,
00419 AST_APP_ARG(field)[100];
00420 );
00421 SQLHSTMT stmt = NULL;
00422 SQLSMALLINT colcount=0;
00423 SQLLEN indicator;
00424 SQLSMALLINT collength;
00425 struct odbc_datastore *resultset = NULL;
00426 struct odbc_datastore_row *row = NULL;
00427 struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
00428 const char *status = "FAILURE";
00429
00430 if (!sql || !colnames) {
00431 if (chan) {
00432 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00433 }
00434 return -1;
00435 }
00436
00437 ast_str_reset(colnames);
00438
00439 AST_RWLIST_RDLOCK(&queries);
00440 AST_RWLIST_TRAVERSE(&queries, query, list) {
00441 if (!strcmp(query->acf->name, cmd)) {
00442 break;
00443 }
00444 }
00445
00446 if (!query) {
00447 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00448 AST_RWLIST_UNLOCK(&queries);
00449 if (chan) {
00450 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00451 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00452 }
00453 return -1;
00454 }
00455
00456 if (!chan) {
00457 if (!(chan = ast_dummy_channel_alloc())) {
00458 AST_RWLIST_UNLOCK(&queries);
00459 return -1;
00460 }
00461 bogus_chan = 1;
00462 }
00463
00464 if (!bogus_chan) {
00465 ast_autoservice_start(chan);
00466 }
00467
00468 AST_STANDARD_APP_ARGS(args, s);
00469 for (x = 0; x < args.argc; x++) {
00470 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00471 pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00472 }
00473
00474 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
00475
00476 if (bogus_chan) {
00477 chan = ast_channel_unref(chan);
00478 } else {
00479
00480 for (x = 0; x < args.argc; x++) {
00481 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00482 pbx_builtin_setvar_helper(chan, varname, NULL);
00483 }
00484 }
00485
00486
00487 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00488 if (!bogus_chan && ast_test_flag(query, OPT_MULTIROW)) {
00489 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
00490 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00491 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00492 ast_autoservice_stop(chan);
00493 return -1;
00494 }
00495 AST_LIST_HEAD_INIT(resultset);
00496 if (query->rowlimit) {
00497 rowlimit = query->rowlimit;
00498 } else {
00499 rowlimit = INT_MAX;
00500 }
00501 multirow = 1;
00502 } else if (!bogus_chan) {
00503 if (query->rowlimit > 1) {
00504 rowlimit = query->rowlimit;
00505 if (!(resultset = ast_calloc(1, sizeof(*resultset)))) {
00506 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00507 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00508 ast_autoservice_stop(chan);
00509 return -1;
00510 }
00511 AST_LIST_HEAD_INIT(resultset);
00512 }
00513 }
00514 AST_RWLIST_UNLOCK(&queries);
00515
00516 for (dsn = 0; dsn < 5; dsn++) {
00517 if (!ast_strlen_zero(query->readhandle[dsn])) {
00518 obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
00519 if (obj) {
00520 stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql));
00521 }
00522 }
00523 if (stmt) {
00524 break;
00525 }
00526 if (obj) {
00527 ast_odbc_release_obj(obj);
00528 obj = NULL;
00529 }
00530 }
00531
00532 if (!stmt) {
00533 ast_log(LOG_ERROR, "Unable to execute query [%s]\n", ast_str_buffer(sql));
00534 if (obj) {
00535 ast_odbc_release_obj(obj);
00536 obj = NULL;
00537 }
00538 if (!bogus_chan) {
00539 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00540 ast_autoservice_stop(chan);
00541 }
00542 return -1;
00543 }
00544
00545 res = SQLNumResultCols(stmt, &colcount);
00546 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00547 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
00548 SQLCloseCursor(stmt);
00549 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00550 ast_odbc_release_obj(obj);
00551 obj = NULL;
00552 if (!bogus_chan) {
00553 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00554 ast_autoservice_stop(chan);
00555 }
00556 return -1;
00557 }
00558
00559 res = SQLFetch(stmt);
00560 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00561 int res1 = -1;
00562 if (res == SQL_NO_DATA) {
00563 ast_verb(4, "Found no rows [%s]\n", ast_str_buffer(sql));
00564 res1 = 0;
00565 buf[0] = '\0';
00566 ast_copy_string(rowcount, "0", sizeof(rowcount));
00567 status = "NODATA";
00568 } else {
00569 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
00570 status = "FETCHERROR";
00571 }
00572 SQLCloseCursor(stmt);
00573 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00574 ast_odbc_release_obj(obj);
00575 obj = NULL;
00576 if (!bogus_chan) {
00577 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00578 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00579 ast_autoservice_stop(chan);
00580 }
00581 return res1;
00582 }
00583
00584 status = "SUCCESS";
00585
00586 for (y = 0; y < rowlimit; y++) {
00587 buf[0] = '\0';
00588 for (x = 0; x < colcount; x++) {
00589 int i;
00590 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00591 char *ptrcoldata;
00592
00593 if (!coldata) {
00594 ast_free(resultset);
00595 SQLCloseCursor(stmt);
00596 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00597 ast_odbc_release_obj(obj);
00598 obj = NULL;
00599 if (!bogus_chan) {
00600 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00601 ast_autoservice_stop(chan);
00602 }
00603 return -1;
00604 }
00605
00606 if (y == 0) {
00607 char colname[256];
00608 SQLULEN maxcol = 0;
00609
00610 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00611 ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00612 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00613 snprintf(colname, sizeof(colname), "field%d", x);
00614 }
00615
00616 ast_str_make_space(&coldata, maxcol + 1);
00617
00618 if (ast_str_strlen(colnames)) {
00619 ast_str_append(&colnames, 0, ",");
00620 }
00621 ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
00622
00623 if (resultset) {
00624 void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
00625 if (!tmp) {
00626 ast_log(LOG_ERROR, "No space for a new resultset?\n");
00627 ast_free(resultset);
00628 SQLCloseCursor(stmt);
00629 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00630 ast_odbc_release_obj(obj);
00631 obj = NULL;
00632 if (!bogus_chan) {
00633 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00634 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00635 ast_autoservice_stop(chan);
00636 }
00637 return -1;
00638 }
00639 resultset = tmp;
00640 strcpy((char *)resultset + sizeof(*resultset), ast_str_buffer(colnames));
00641 }
00642 }
00643
00644 buflen = strlen(buf);
00645 res = ast_odbc_ast_str_SQLGetData(&coldata, -1, stmt, x + 1, SQL_CHAR, &indicator);
00646 if (indicator == SQL_NULL_DATA) {
00647 ast_debug(3, "Got NULL data\n");
00648 ast_str_reset(coldata);
00649 res = SQL_SUCCESS;
00650 }
00651
00652 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00653 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", ast_str_buffer(sql));
00654 y = -1;
00655 buf[0] = '\0';
00656 goto end_acf_read;
00657 }
00658
00659 ast_debug(2, "Got coldata of '%s'\n", ast_str_buffer(coldata));
00660
00661 if (x) {
00662 buf[buflen++] = ',';
00663 }
00664
00665
00666 ptrcoldata = ast_str_buffer(coldata);
00667 for (i = 0; i < ast_str_strlen(coldata); i++) {
00668 if (escapecommas && (ptrcoldata[i] == '\\' || ptrcoldata[i] == ',')) {
00669 buf[buflen++] = '\\';
00670 }
00671 buf[buflen++] = ptrcoldata[i];
00672
00673 if (buflen >= len - 2) {
00674 break;
00675 }
00676
00677 if (ptrcoldata[i] == '\0') {
00678 break;
00679 }
00680 }
00681
00682 buf[buflen] = '\0';
00683 ast_debug(2, "buf is now set to '%s'\n", buf);
00684 }
00685 ast_debug(2, "buf is now set to '%s'\n", buf);
00686
00687 if (resultset) {
00688 row = ast_calloc(1, sizeof(*row) + buflen + 1);
00689 if (!row) {
00690 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00691 status = "MEMERROR";
00692 goto end_acf_read;
00693 }
00694 strcpy((char *)row + sizeof(*row), buf);
00695 AST_LIST_INSERT_TAIL(resultset, row, list);
00696
00697
00698 res = SQLFetch(stmt);
00699 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00700 if (res != SQL_NO_DATA) {
00701 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
00702 }
00703
00704 y++;
00705 break;
00706 }
00707 }
00708 }
00709
00710 end_acf_read:
00711 if (!bogus_chan) {
00712 snprintf(rowcount, sizeof(rowcount), "%d", y);
00713 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00714 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", status);
00715 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
00716 if (resultset) {
00717 int uid;
00718 struct ast_datastore *odbc_store;
00719 if (multirow) {
00720 uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00721 snprintf(buf, len, "%d", uid);
00722 } else {
00723
00724 ast_copy_string(buf, cmd, len);
00725
00726
00727 ast_channel_lock(chan);
00728 if ((odbc_store = ast_channel_datastore_find(chan, &odbc_info, buf))) {
00729 ast_channel_datastore_remove(chan, odbc_store);
00730 ast_datastore_free(odbc_store);
00731 }
00732 ast_channel_unlock(chan);
00733 }
00734 odbc_store = ast_datastore_alloc(&odbc_info, buf);
00735 if (!odbc_store) {
00736 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
00737 odbc_datastore_free(resultset);
00738 SQLCloseCursor(stmt);
00739 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00740 ast_odbc_release_obj(obj);
00741 obj = NULL;
00742 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00743 ast_autoservice_stop(chan);
00744 return -1;
00745 }
00746 odbc_store->data = resultset;
00747 ast_channel_lock(chan);
00748 ast_channel_datastore_add(chan, odbc_store);
00749 ast_channel_unlock(chan);
00750 }
00751 }
00752 SQLCloseCursor(stmt);
00753 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00754 ast_odbc_release_obj(obj);
00755 obj = NULL;
00756 if (resultset && !multirow) {
00757
00758 if (!acf_fetch(chan, "", buf, buf, len)) {
00759 buf[0] = '\0';
00760 }
00761 }
00762 if (!bogus_chan) {
00763 ast_autoservice_stop(chan);
00764 }
00765 return 0;
00766 }
00767
00768 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00769 {
00770 char *out = buf;
00771
00772 for (; *data && out - buf < len; data++) {
00773 if (*data == '\'') {
00774 *out = '\'';
00775 out++;
00776 }
00777 *out++ = *data;
00778 }
00779 *out = '\0';
00780
00781 return 0;
00782 }
00783
00784 static struct ast_custom_function escape_function = {
00785 .name = "SQL_ESC",
00786 .read = acf_escape,
00787 .write = NULL,
00788 };
00789
00790 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00791 {
00792 struct ast_datastore *store;
00793 struct odbc_datastore *resultset;
00794 struct odbc_datastore_row *row;
00795
00796 ast_channel_lock(chan);
00797 store = ast_channel_datastore_find(chan, &odbc_info, data);
00798 if (!store) {
00799 ast_channel_unlock(chan);
00800 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00801 return -1;
00802 }
00803 resultset = store->data;
00804 AST_LIST_LOCK(resultset);
00805 row = AST_LIST_REMOVE_HEAD(resultset, list);
00806 AST_LIST_UNLOCK(resultset);
00807 if (!row) {
00808
00809 ast_channel_datastore_remove(chan, store);
00810 ast_datastore_free(store);
00811 ast_channel_unlock(chan);
00812 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00813 return -1;
00814 }
00815 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00816 ast_channel_unlock(chan);
00817 ast_copy_string(buf, row->data, len);
00818 ast_free(row);
00819 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
00820 return 0;
00821 }
00822
00823 static struct ast_custom_function fetch_function = {
00824 .name = "ODBC_FETCH",
00825 .read = acf_fetch,
00826 .write = NULL,
00827 };
00828
00829 static char *app_odbcfinish = "ODBCFinish";
00830
00831 static int exec_odbcfinish(struct ast_channel *chan, const char *data)
00832 {
00833 struct ast_datastore *store;
00834
00835 ast_channel_lock(chan);
00836 store = ast_channel_datastore_find(chan, &odbc_info, data);
00837 if (store) {
00838 ast_channel_datastore_remove(chan, store);
00839 ast_datastore_free(store);
00840 }
00841 ast_channel_unlock(chan);
00842 return 0;
00843 }
00844
00845 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00846 {
00847 const char *tmp;
00848 int i;
00849
00850 if (!cfg || !catg) {
00851 return EINVAL;
00852 }
00853
00854 *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00855 if (! (*query))
00856 return ENOMEM;
00857
00858 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00859 char *tmp2 = ast_strdupa(tmp);
00860 AST_DECLARE_APP_ARGS(writeconf,
00861 AST_APP_ARG(dsn)[5];
00862 );
00863 AST_STANDARD_APP_ARGS(writeconf, tmp2);
00864 for (i = 0; i < 5; i++) {
00865 if (!ast_strlen_zero(writeconf.dsn[i]))
00866 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00867 }
00868 }
00869
00870 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00871 char *tmp2 = ast_strdupa(tmp);
00872 AST_DECLARE_APP_ARGS(readconf,
00873 AST_APP_ARG(dsn)[5];
00874 );
00875 AST_STANDARD_APP_ARGS(readconf, tmp2);
00876 for (i = 0; i < 5; i++) {
00877 if (!ast_strlen_zero(readconf.dsn[i]))
00878 ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00879 }
00880 } else {
00881
00882 for (i = 0; i < 5; i++) {
00883 if (!ast_strlen_zero((*query)->writehandle[i]))
00884 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00885 }
00886 }
00887
00888 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00889 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00890 else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00891 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
00892 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00893 }
00894
00895 if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00896 ast_free(*query);
00897 *query = NULL;
00898 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00899 return EINVAL;
00900 }
00901
00902 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00903 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00904 else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00905 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
00906 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00907 }
00908
00909 if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00910 ast_free(*query);
00911 *query = NULL;
00912 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00913 return EINVAL;
00914 }
00915
00916 if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
00917 ast_copy_string((*query)->sql_insert, tmp, sizeof((*query)->sql_insert));
00918 }
00919
00920
00921 ast_set_flag((*query), OPT_ESCAPECOMMAS);
00922 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00923 if (ast_false(tmp))
00924 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00925 }
00926
00927 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00928 if (strcasecmp(tmp, "multirow") == 0)
00929 ast_set_flag((*query), OPT_MULTIROW);
00930 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00931 sscanf(tmp, "%30d", &((*query)->rowlimit));
00932 }
00933
00934 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00935 if (! (*query)->acf) {
00936 ast_free(*query);
00937 *query = NULL;
00938 return ENOMEM;
00939 }
00940 if (ast_string_field_init((*query)->acf, 128)) {
00941 ast_free((*query)->acf);
00942 ast_free(*query);
00943 *query = NULL;
00944 return ENOMEM;
00945 }
00946
00947 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00948 if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00949 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00950 }
00951 } else {
00952 if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00953 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00954 }
00955 }
00956
00957 if (!((*query)->acf->name)) {
00958 ast_string_field_free_memory((*query)->acf);
00959 ast_free((*query)->acf);
00960 ast_free(*query);
00961 *query = NULL;
00962 return ENOMEM;
00963 }
00964
00965 if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
00966 ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
00967 } else {
00968 ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
00969 }
00970
00971 if (ast_strlen_zero((*query)->acf->syntax)) {
00972 ast_free((char *)(*query)->acf->name);
00973 ast_string_field_free_memory((*query)->acf);
00974 ast_free((*query)->acf);
00975 ast_free(*query);
00976 *query = NULL;
00977 return ENOMEM;
00978 }
00979
00980 if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
00981 ast_string_field_set((*query)->acf, synopsis, tmp);
00982 } else {
00983 ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
00984 }
00985
00986 if (ast_strlen_zero((*query)->acf->synopsis)) {
00987 ast_free((char *)(*query)->acf->name);
00988 ast_string_field_free_memory((*query)->acf);
00989 ast_free((*query)->acf);
00990 ast_free(*query);
00991 *query = NULL;
00992 return ENOMEM;
00993 }
00994
00995 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00996 ast_string_field_build((*query)->acf, desc,
00997 "Runs the following query, as defined in func_odbc.conf, performing\n"
00998 "substitution of the arguments into the query as specified by ${ARG1},\n"
00999 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
01000 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
01001 "%s"
01002 "\nRead:\n%s\n\nWrite:\n%s\n%s%s%s",
01003 ast_strlen_zero((*query)->sql_insert) ? "" :
01004 "If the write query affects no rows, the insert query will be\n"
01005 "performed.\n",
01006 (*query)->sql_read,
01007 (*query)->sql_write,
01008 ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
01009 ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
01010 ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
01011 } else if (!ast_strlen_zero((*query)->sql_read)) {
01012 ast_string_field_build((*query)->acf, desc,
01013 "Runs the following query, as defined in func_odbc.conf, performing\n"
01014 "substitution of the arguments into the query as specified by ${ARG1},\n"
01015 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
01016 (*query)->sql_read);
01017 } else if (!ast_strlen_zero((*query)->sql_write)) {
01018 ast_string_field_build((*query)->acf, desc,
01019 "Runs the following query, as defined in func_odbc.conf, performing\n"
01020 "substitution of the arguments into the query as specified by ${ARG1},\n"
01021 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
01022 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
01023 "This function may only be set.\n%sSQL:\n%s\n%s%s%s",
01024 ast_strlen_zero((*query)->sql_insert) ? "" :
01025 "If the write query affects no rows, the insert query will be\n"
01026 "performed.\n",
01027 (*query)->sql_write,
01028 ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
01029 ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
01030 ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
01031 } else {
01032 ast_string_field_free_memory((*query)->acf);
01033 ast_free((char *)(*query)->acf->name);
01034 ast_free((*query)->acf);
01035 ast_free(*query);
01036 ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg);
01037 return EINVAL;
01038 }
01039
01040 if (ast_strlen_zero((*query)->acf->desc)) {
01041 ast_string_field_free_memory((*query)->acf);
01042 ast_free((char *)(*query)->acf->name);
01043 ast_free((*query)->acf);
01044 ast_free(*query);
01045 *query = NULL;
01046 return ENOMEM;
01047 }
01048
01049 if (ast_strlen_zero((*query)->sql_read)) {
01050 (*query)->acf->read = NULL;
01051 } else {
01052 (*query)->acf->read = acf_odbc_read;
01053 }
01054
01055 if (ast_strlen_zero((*query)->sql_write)) {
01056 (*query)->acf->write = NULL;
01057 } else {
01058 (*query)->acf->write = acf_odbc_write;
01059 }
01060
01061 return 0;
01062 }
01063
01064 static int free_acf_query(struct acf_odbc_query *query)
01065 {
01066 if (query) {
01067 if (query->acf) {
01068 if (query->acf->name)
01069 ast_free((char *)query->acf->name);
01070 ast_string_field_free_memory(query->acf);
01071 ast_free(query->acf);
01072 }
01073 ast_free(query);
01074 }
01075 return 0;
01076 }
01077
01078 static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01079 {
01080 AST_DECLARE_APP_ARGS(args,
01081 AST_APP_ARG(field)[100];
01082 );
01083 struct ast_str *sql;
01084 char *char_args, varname[10];
01085 struct acf_odbc_query *query;
01086 struct ast_channel *chan;
01087 int i;
01088
01089 switch (cmd) {
01090 case CLI_INIT:
01091 e->command = "odbc read";
01092 e->usage =
01093 "Usage: odbc read <name> <args> [exec]\n"
01094 " Evaluates the SQL provided in the ODBC function <name>, and\n"
01095 " optionally executes the function. This function is intended for\n"
01096 " testing purposes. Remember to quote arguments containing spaces.\n";
01097 return NULL;
01098 case CLI_GENERATE:
01099 if (a->pos == 2) {
01100 int wordlen = strlen(a->word), which = 0;
01101
01102 AST_RWLIST_RDLOCK(&queries);
01103 AST_RWLIST_TRAVERSE(&queries, query, list) {
01104 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01105 if (++which > a->n) {
01106 char *res = ast_strdup(query->acf->name);
01107 AST_RWLIST_UNLOCK(&queries);
01108 return res;
01109 }
01110 }
01111 }
01112 AST_RWLIST_UNLOCK(&queries);
01113 return NULL;
01114 } else if (a->pos == 4) {
01115 return a->n == 0 ? ast_strdup("exec") : NULL;
01116 } else {
01117 return NULL;
01118 }
01119 }
01120
01121 if (a->argc < 4 || a->argc > 5) {
01122 return CLI_SHOWUSAGE;
01123 }
01124
01125 sql = ast_str_thread_get(&sql_buf, 16);
01126 if (!sql) {
01127 return CLI_FAILURE;
01128 }
01129
01130 AST_RWLIST_RDLOCK(&queries);
01131 AST_RWLIST_TRAVERSE(&queries, query, list) {
01132 if (!strcmp(query->acf->name, a->argv[2])) {
01133 break;
01134 }
01135 }
01136
01137 if (!query) {
01138 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01139 AST_RWLIST_UNLOCK(&queries);
01140 return CLI_SHOWUSAGE;
01141 }
01142
01143 if (ast_strlen_zero(query->sql_read)) {
01144 ast_cli(a->fd, "The function %s has no readsql parameter.\n", a->argv[2]);
01145 AST_RWLIST_UNLOCK(&queries);
01146 return CLI_SUCCESS;
01147 }
01148
01149 ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
01150
01151
01152 char_args = ast_strdupa(a->argv[3]);
01153
01154 chan = ast_dummy_channel_alloc();
01155 if (!chan) {
01156 AST_RWLIST_UNLOCK(&queries);
01157 return CLI_FAILURE;
01158 }
01159
01160 AST_STANDARD_APP_ARGS(args, char_args);
01161 for (i = 0; i < args.argc; i++) {
01162 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01163 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01164 }
01165
01166 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
01167 chan = ast_channel_unref(chan);
01168
01169 if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
01170
01171 struct odbc_obj *obj = NULL;
01172 int dsn, executed = 0;
01173 SQLHSTMT stmt;
01174 int rows = 0, res, x;
01175 SQLSMALLINT colcount = 0, collength;
01176 SQLLEN indicator;
01177 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
01178 char colname[256];
01179 SQLULEN maxcol;
01180
01181 if (!coldata) {
01182 AST_RWLIST_UNLOCK(&queries);
01183 return CLI_SUCCESS;
01184 }
01185
01186 for (dsn = 0; dsn < 5; dsn++) {
01187 if (ast_strlen_zero(query->readhandle[dsn])) {
01188 continue;
01189 }
01190 ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
01191 if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) {
01192 continue;
01193 }
01194
01195 ast_debug(1, "Got obj\n");
01196 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01197 ast_odbc_release_obj(obj);
01198 obj = NULL;
01199 continue;
01200 }
01201
01202 executed = 1;
01203
01204 res = SQLNumResultCols(stmt, &colcount);
01205 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01206 ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
01207 SQLCloseCursor(stmt);
01208 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01209 ast_odbc_release_obj(obj);
01210 obj = NULL;
01211 AST_RWLIST_UNLOCK(&queries);
01212 return CLI_SUCCESS;
01213 }
01214
01215 res = SQLFetch(stmt);
01216 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01217 SQLCloseCursor(stmt);
01218 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01219 ast_odbc_release_obj(obj);
01220 obj = NULL;
01221 if (res == SQL_NO_DATA) {
01222 ast_cli(a->fd, "Returned %d rows. Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql));
01223 break;
01224 } else {
01225 ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
01226 }
01227 AST_RWLIST_UNLOCK(&queries);
01228 return CLI_SUCCESS;
01229 }
01230 for (;;) {
01231 for (x = 0; x < colcount; x++) {
01232 maxcol = 0;
01233
01234 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
01235 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
01236 snprintf(colname, sizeof(colname), "field%d", x);
01237 }
01238
01239 res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
01240 if (indicator == SQL_NULL_DATA) {
01241 ast_str_set(&coldata, 0, "(nil)");
01242 res = SQL_SUCCESS;
01243 }
01244
01245 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01246 ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
01247 SQLCloseCursor(stmt);
01248 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01249 ast_odbc_release_obj(obj);
01250 obj = NULL;
01251 AST_RWLIST_UNLOCK(&queries);
01252 return CLI_SUCCESS;
01253 }
01254
01255 ast_cli(a->fd, "%-20.20s %s\n", colname, ast_str_buffer(coldata));
01256 }
01257 rows++;
01258
01259
01260 res = SQLFetch(stmt);
01261 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01262 break;
01263 }
01264 ast_cli(a->fd, "%-20.20s %s\n", "----------", "----------");
01265 }
01266 SQLCloseCursor(stmt);
01267 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01268 ast_odbc_release_obj(obj);
01269 obj = NULL;
01270 ast_cli(a->fd, "Returned %d row%s. Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
01271 break;
01272 }
01273 if (obj) {
01274 ast_odbc_release_obj(obj);
01275 obj = NULL;
01276 }
01277
01278 if (!executed) {
01279 ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
01280 }
01281 } else {
01282 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01283 }
01284 AST_RWLIST_UNLOCK(&queries);
01285 return CLI_SUCCESS;
01286 }
01287
01288 static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01289 {
01290 AST_DECLARE_APP_ARGS(values,
01291 AST_APP_ARG(field)[100];
01292 );
01293 AST_DECLARE_APP_ARGS(args,
01294 AST_APP_ARG(field)[100];
01295 );
01296 struct ast_str *sql;
01297 char *char_args, *char_values, varname[10];
01298 struct acf_odbc_query *query;
01299 struct ast_channel *chan;
01300 int i;
01301
01302 switch (cmd) {
01303 case CLI_INIT:
01304 e->command = "odbc write";
01305 e->usage =
01306 "Usage: odbc write <name> <args> <value> [exec]\n"
01307 " Evaluates the SQL provided in the ODBC function <name>, and\n"
01308 " optionally executes the function. This function is intended for\n"
01309 " testing purposes. Remember to quote arguments containing spaces.\n";
01310 return NULL;
01311 case CLI_GENERATE:
01312 if (a->pos == 2) {
01313 int wordlen = strlen(a->word), which = 0;
01314
01315 AST_RWLIST_RDLOCK(&queries);
01316 AST_RWLIST_TRAVERSE(&queries, query, list) {
01317 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01318 if (++which > a->n) {
01319 char *res = ast_strdup(query->acf->name);
01320 AST_RWLIST_UNLOCK(&queries);
01321 return res;
01322 }
01323 }
01324 }
01325 AST_RWLIST_UNLOCK(&queries);
01326 return NULL;
01327 } else if (a->pos == 5) {
01328 return a->n == 0 ? ast_strdup("exec") : NULL;
01329 } else {
01330 return NULL;
01331 }
01332 }
01333
01334 if (a->argc < 5 || a->argc > 6) {
01335 return CLI_SHOWUSAGE;
01336 }
01337
01338 sql = ast_str_thread_get(&sql_buf, 16);
01339 if (!sql) {
01340 return CLI_FAILURE;
01341 }
01342
01343 AST_RWLIST_RDLOCK(&queries);
01344 AST_RWLIST_TRAVERSE(&queries, query, list) {
01345 if (!strcmp(query->acf->name, a->argv[2])) {
01346 break;
01347 }
01348 }
01349
01350 if (!query) {
01351 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01352 AST_RWLIST_UNLOCK(&queries);
01353 return CLI_SHOWUSAGE;
01354 }
01355
01356 if (ast_strlen_zero(query->sql_write)) {
01357 ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
01358 AST_RWLIST_UNLOCK(&queries);
01359 return CLI_SUCCESS;
01360 }
01361
01362 ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
01363
01364
01365 char_args = ast_strdupa(a->argv[3]);
01366 char_values = ast_strdupa(a->argv[4]);
01367
01368 chan = ast_dummy_channel_alloc();
01369 if (!chan) {
01370 AST_RWLIST_UNLOCK(&queries);
01371 return CLI_FAILURE;
01372 }
01373
01374 AST_STANDARD_APP_ARGS(args, char_args);
01375 for (i = 0; i < args.argc; i++) {
01376 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01377 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01378 }
01379
01380
01381 AST_STANDARD_APP_ARGS(values, char_values);
01382 for (i = 0; i < values.argc; i++) {
01383 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
01384 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
01385 }
01386
01387
01388 pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
01389 ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
01390 ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
01391
01392 chan = ast_channel_unref(chan);
01393
01394 if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
01395
01396 struct odbc_obj *obj = NULL;
01397 int dsn, executed = 0;
01398 SQLHSTMT stmt;
01399 SQLLEN rows = -1;
01400
01401 for (dsn = 0; dsn < 5; dsn++) {
01402 if (ast_strlen_zero(query->writehandle[dsn])) {
01403 continue;
01404 }
01405 if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) {
01406 continue;
01407 }
01408 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01409 ast_odbc_release_obj(obj);
01410 obj = NULL;
01411 continue;
01412 }
01413
01414 SQLRowCount(stmt, &rows);
01415 SQLCloseCursor(stmt);
01416 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01417 ast_odbc_release_obj(obj);
01418 obj = NULL;
01419 ast_cli(a->fd, "Affected %d rows. Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]);
01420 executed = 1;
01421 break;
01422 }
01423
01424 if (!executed) {
01425 ast_cli(a->fd, "Failed to execute query.\n");
01426 }
01427 } else {
01428 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01429 }
01430 AST_RWLIST_UNLOCK(&queries);
01431 return CLI_SUCCESS;
01432 }
01433
01434 static struct ast_cli_entry cli_func_odbc[] = {
01435 AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
01436 AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
01437 };
01438
01439 static int load_module(void)
01440 {
01441 int res = 0;
01442 struct ast_config *cfg;
01443 char *catg;
01444 struct ast_flags config_flags = { 0 };
01445
01446 res |= ast_custom_function_register(&fetch_function);
01447 res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
01448 AST_RWLIST_WRLOCK(&queries);
01449
01450 cfg = ast_config_load(config, config_flags);
01451 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
01452 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
01453 AST_RWLIST_UNLOCK(&queries);
01454 return AST_MODULE_LOAD_DECLINE;
01455 }
01456
01457 for (catg = ast_category_browse(cfg, NULL);
01458 catg;
01459 catg = ast_category_browse(cfg, catg)) {
01460 struct acf_odbc_query *query = NULL;
01461 int err;
01462
01463 if ((err = init_acf_query(cfg, catg, &query))) {
01464 if (err == ENOMEM)
01465 ast_log(LOG_ERROR, "Out of memory\n");
01466 else if (err == EINVAL)
01467 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
01468 else
01469 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
01470 } else {
01471 AST_RWLIST_INSERT_HEAD(&queries, query, list);
01472 ast_custom_function_register(query->acf);
01473 }
01474 }
01475
01476 ast_config_destroy(cfg);
01477 res |= ast_custom_function_register(&escape_function);
01478 ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01479
01480 AST_RWLIST_UNLOCK(&queries);
01481 return res;
01482 }
01483
01484 static int unload_module(void)
01485 {
01486 struct acf_odbc_query *query;
01487 int res = 0;
01488
01489 AST_RWLIST_WRLOCK(&queries);
01490 while (!AST_RWLIST_EMPTY(&queries)) {
01491 query = AST_RWLIST_REMOVE_HEAD(&queries, list);
01492 ast_custom_function_unregister(query->acf);
01493 free_acf_query(query);
01494 }
01495
01496 res |= ast_custom_function_unregister(&escape_function);
01497 res |= ast_custom_function_unregister(&fetch_function);
01498 res |= ast_unregister_application(app_odbcfinish);
01499 ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01500
01501
01502 AST_RWLIST_UNLOCK(&queries);
01503 usleep(1);
01504 AST_RWLIST_WRLOCK(&queries);
01505
01506 AST_RWLIST_UNLOCK(&queries);
01507 return 0;
01508 }
01509
01510 static int reload(void)
01511 {
01512 int res = 0;
01513 struct ast_config *cfg;
01514 struct acf_odbc_query *oldquery;
01515 char *catg;
01516 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
01517
01518 cfg = ast_config_load(config, config_flags);
01519 if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
01520 return 0;
01521
01522 AST_RWLIST_WRLOCK(&queries);
01523
01524 while (!AST_RWLIST_EMPTY(&queries)) {
01525 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
01526 ast_custom_function_unregister(oldquery->acf);
01527 free_acf_query(oldquery);
01528 }
01529
01530 if (!cfg) {
01531 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
01532 goto reload_out;
01533 }
01534
01535 for (catg = ast_category_browse(cfg, NULL);
01536 catg;
01537 catg = ast_category_browse(cfg, catg)) {
01538 struct acf_odbc_query *query = NULL;
01539
01540 if (init_acf_query(cfg, catg, &query)) {
01541 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
01542 } else {
01543 AST_RWLIST_INSERT_HEAD(&queries, query, list);
01544 ast_custom_function_register(query->acf);
01545 }
01546 }
01547
01548 ast_config_destroy(cfg);
01549 reload_out:
01550 AST_RWLIST_UNLOCK(&queries);
01551 return res;
01552 }
01553
01554
01555
01556 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
01557 .load = load_module,
01558 .unload = unload_module,
01559 .reload = reload,
01560 );
01561