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: 328540 $")
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_release(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_release(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_release(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 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00600 if (chan)
00601 ast_autoservice_stop(chan);
00602 if (bogus_chan) {
00603 ast_channel_release(chan);
00604 }
00605 return -1;
00606 }
00607
00608 if (y == 0) {
00609 char colname[256];
00610 SQLULEN maxcol = 0;
00611
00612 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00613 ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00614 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00615 snprintf(colname, sizeof(colname), "field%d", x);
00616 }
00617
00618 ast_str_make_space(&coldata, maxcol + 1);
00619
00620 if (ast_str_strlen(colnames)) {
00621 ast_str_append(&colnames, 0, ",");
00622 }
00623 ast_str_append_escapecommas(&colnames, 0, colname, sizeof(colname));
00624
00625 if (resultset) {
00626 void *tmp = ast_realloc(resultset, sizeof(*resultset) + ast_str_strlen(colnames) + 1);
00627 if (!tmp) {
00628 ast_log(LOG_ERROR, "No space for a new resultset?\n");
00629 ast_free(resultset);
00630 SQLCloseCursor(stmt);
00631 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00632 ast_odbc_release_obj(obj);
00633 obj = NULL;
00634 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00635 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00636 ast_autoservice_stop(chan);
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 odbc_datastore_free(odbc_store->data);
00731 ast_free(odbc_store);
00732 }
00733 ast_channel_unlock(chan);
00734 }
00735 odbc_store = ast_datastore_alloc(&odbc_info, buf);
00736 if (!odbc_store) {
00737 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
00738 odbc_datastore_free(resultset);
00739 SQLCloseCursor(stmt);
00740 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00741 ast_odbc_release_obj(obj);
00742 obj = NULL;
00743 pbx_builtin_setvar_helper(chan, "ODBCSTATUS", "MEMERROR");
00744 ast_autoservice_stop(chan);
00745 return -1;
00746 }
00747 odbc_store->data = resultset;
00748 ast_channel_datastore_add(chan, odbc_store);
00749 }
00750 }
00751 SQLCloseCursor(stmt);
00752 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00753 ast_odbc_release_obj(obj);
00754 obj = NULL;
00755 if (resultset && !multirow) {
00756
00757 if (!acf_fetch(chan, "", buf, buf, len)) {
00758 buf[0] = '\0';
00759 }
00760 }
00761 if (!bogus_chan) {
00762 ast_autoservice_stop(chan);
00763 }
00764 return 0;
00765 }
00766
00767 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00768 {
00769 char *out = buf;
00770
00771 for (; *data && out - buf < len; data++) {
00772 if (*data == '\'') {
00773 *out = '\'';
00774 out++;
00775 }
00776 *out++ = *data;
00777 }
00778 *out = '\0';
00779
00780 return 0;
00781 }
00782
00783 static struct ast_custom_function escape_function = {
00784 .name = "SQL_ESC",
00785 .read = acf_escape,
00786 .write = NULL,
00787 };
00788
00789 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00790 {
00791 struct ast_datastore *store;
00792 struct odbc_datastore *resultset;
00793 struct odbc_datastore_row *row;
00794 store = ast_channel_datastore_find(chan, &odbc_info, data);
00795 if (!store) {
00796 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00797 return -1;
00798 }
00799 resultset = store->data;
00800 AST_LIST_LOCK(resultset);
00801 row = AST_LIST_REMOVE_HEAD(resultset, list);
00802 AST_LIST_UNLOCK(resultset);
00803 if (!row) {
00804
00805 ast_channel_datastore_remove(chan, store);
00806 ast_datastore_free(store);
00807 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "FAILURE");
00808 return -1;
00809 }
00810 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00811 ast_copy_string(buf, row->data, len);
00812 ast_free(row);
00813 pbx_builtin_setvar_helper(chan, "ODBC_FETCH_STATUS", "SUCCESS");
00814 return 0;
00815 }
00816
00817 static struct ast_custom_function fetch_function = {
00818 .name = "ODBC_FETCH",
00819 .read = acf_fetch,
00820 .write = NULL,
00821 };
00822
00823 static char *app_odbcfinish = "ODBCFinish";
00824
00825 static int exec_odbcfinish(struct ast_channel *chan, const char *data)
00826 {
00827 struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00828 if (!store)
00829 return 0;
00830 ast_channel_datastore_remove(chan, store);
00831 ast_datastore_free(store);
00832 return 0;
00833 }
00834
00835 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00836 {
00837 const char *tmp;
00838 int i;
00839
00840 if (!cfg || !catg) {
00841 return EINVAL;
00842 }
00843
00844 *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00845 if (! (*query))
00846 return ENOMEM;
00847
00848 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00849 char *tmp2 = ast_strdupa(tmp);
00850 AST_DECLARE_APP_ARGS(writeconf,
00851 AST_APP_ARG(dsn)[5];
00852 );
00853 AST_STANDARD_APP_ARGS(writeconf, tmp2);
00854 for (i = 0; i < 5; i++) {
00855 if (!ast_strlen_zero(writeconf.dsn[i]))
00856 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00857 }
00858 }
00859
00860 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00861 char *tmp2 = ast_strdupa(tmp);
00862 AST_DECLARE_APP_ARGS(readconf,
00863 AST_APP_ARG(dsn)[5];
00864 );
00865 AST_STANDARD_APP_ARGS(readconf, tmp2);
00866 for (i = 0; i < 5; i++) {
00867 if (!ast_strlen_zero(readconf.dsn[i]))
00868 ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00869 }
00870 } else {
00871
00872 for (i = 0; i < 5; i++) {
00873 if (!ast_strlen_zero((*query)->writehandle[i]))
00874 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00875 }
00876 }
00877
00878 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00879 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00880 else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00881 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
00882 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00883 }
00884
00885 if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00886 ast_free(*query);
00887 *query = NULL;
00888 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00889 return EINVAL;
00890 }
00891
00892 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00893 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00894 else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00895 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
00896 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00897 }
00898
00899 if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00900 ast_free(*query);
00901 *query = NULL;
00902 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00903 return EINVAL;
00904 }
00905
00906 if ((tmp = ast_variable_retrieve(cfg, catg, "insertsql"))) {
00907 ast_copy_string((*query)->sql_insert, tmp, sizeof((*query)->sql_insert));
00908 }
00909
00910
00911 ast_set_flag((*query), OPT_ESCAPECOMMAS);
00912 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00913 if (ast_false(tmp))
00914 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00915 }
00916
00917 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00918 if (strcasecmp(tmp, "multirow") == 0)
00919 ast_set_flag((*query), OPT_MULTIROW);
00920 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00921 sscanf(tmp, "%30d", &((*query)->rowlimit));
00922 }
00923
00924 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00925 if (! (*query)->acf) {
00926 ast_free(*query);
00927 *query = NULL;
00928 return ENOMEM;
00929 }
00930 if (ast_string_field_init((*query)->acf, 128)) {
00931 ast_free((*query)->acf);
00932 ast_free(*query);
00933 *query = NULL;
00934 return ENOMEM;
00935 }
00936
00937 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00938 if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00939 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00940 }
00941 } else {
00942 if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00943 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00944 }
00945 }
00946
00947 if (!((*query)->acf->name)) {
00948 ast_string_field_free_memory((*query)->acf);
00949 ast_free((*query)->acf);
00950 ast_free(*query);
00951 *query = NULL;
00952 return ENOMEM;
00953 }
00954
00955 if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
00956 ast_string_field_build((*query)->acf, syntax, "%s(%s)", (*query)->acf->name, tmp);
00957 } else {
00958 ast_string_field_build((*query)->acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query)->acf->name);
00959 }
00960
00961 if (ast_strlen_zero((*query)->acf->syntax)) {
00962 ast_free((char *)(*query)->acf->name);
00963 ast_string_field_free_memory((*query)->acf);
00964 ast_free((*query)->acf);
00965 ast_free(*query);
00966 *query = NULL;
00967 return ENOMEM;
00968 }
00969
00970 if ((tmp = ast_variable_retrieve(cfg, catg, "synopsis")) && !ast_strlen_zero(tmp)) {
00971 ast_string_field_set((*query)->acf, synopsis, tmp);
00972 } else {
00973 ast_string_field_set((*query)->acf, synopsis, "Runs the referenced query with the specified arguments");
00974 }
00975
00976 if (ast_strlen_zero((*query)->acf->synopsis)) {
00977 ast_free((char *)(*query)->acf->name);
00978 ast_string_field_free_memory((*query)->acf);
00979 ast_free((*query)->acf);
00980 ast_free(*query);
00981 *query = NULL;
00982 return ENOMEM;
00983 }
00984
00985 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00986 ast_string_field_build((*query)->acf, desc,
00987 "Runs the following query, as defined in func_odbc.conf, performing\n"
00988 "substitution of the arguments into the query as specified by ${ARG1},\n"
00989 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
00990 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00991 "%s"
00992 "\nRead:\n%s\n\nWrite:\n%s\n%s%s%s",
00993 ast_strlen_zero((*query)->sql_insert) ? "" :
00994 "If the write query affects no rows, the insert query will be\n"
00995 "performed.\n",
00996 (*query)->sql_read,
00997 (*query)->sql_write,
00998 ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
00999 ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
01000 ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
01001 } else if (!ast_strlen_zero((*query)->sql_read)) {
01002 ast_string_field_build((*query)->acf, desc,
01003 "Runs the following query, as defined in func_odbc.conf, performing\n"
01004 "substitution of the arguments into the query as specified by ${ARG1},\n"
01005 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
01006 (*query)->sql_read);
01007 } else if (!ast_strlen_zero((*query)->sql_write)) {
01008 ast_string_field_build((*query)->acf, desc,
01009 "Runs the following query, as defined in func_odbc.conf, performing\n"
01010 "substitution of the arguments into the query as specified by ${ARG1},\n"
01011 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
01012 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
01013 "This function may only be set.\n%sSQL:\n%s\n%s%s%s",
01014 ast_strlen_zero((*query)->sql_insert) ? "" :
01015 "If the write query affects no rows, the insert query will be\n"
01016 "performed.\n",
01017 (*query)->sql_write,
01018 ast_strlen_zero((*query)->sql_insert) ? "" : "Insert:\n",
01019 ast_strlen_zero((*query)->sql_insert) ? "" : (*query)->sql_insert,
01020 ast_strlen_zero((*query)->sql_insert) ? "" : "\n");
01021 } else {
01022 ast_string_field_free_memory((*query)->acf);
01023 ast_free((char *)(*query)->acf->name);
01024 ast_free((*query)->acf);
01025 ast_free(*query);
01026 ast_log(LOG_WARNING, "Section '%s' was found, but there was no SQL to execute. Ignoring.\n", catg);
01027 return EINVAL;
01028 }
01029
01030 if (ast_strlen_zero((*query)->acf->desc)) {
01031 ast_string_field_free_memory((*query)->acf);
01032 ast_free((char *)(*query)->acf->name);
01033 ast_free((*query)->acf);
01034 ast_free(*query);
01035 *query = NULL;
01036 return ENOMEM;
01037 }
01038
01039 if (ast_strlen_zero((*query)->sql_read)) {
01040 (*query)->acf->read = NULL;
01041 } else {
01042 (*query)->acf->read = acf_odbc_read;
01043 }
01044
01045 if (ast_strlen_zero((*query)->sql_write)) {
01046 (*query)->acf->write = NULL;
01047 } else {
01048 (*query)->acf->write = acf_odbc_write;
01049 }
01050
01051 return 0;
01052 }
01053
01054 static int free_acf_query(struct acf_odbc_query *query)
01055 {
01056 if (query) {
01057 if (query->acf) {
01058 if (query->acf->name)
01059 ast_free((char *)query->acf->name);
01060 ast_string_field_free_memory(query->acf);
01061 ast_free(query->acf);
01062 }
01063 ast_free(query);
01064 }
01065 return 0;
01066 }
01067
01068 static char *cli_odbc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01069 {
01070 AST_DECLARE_APP_ARGS(args,
01071 AST_APP_ARG(field)[100];
01072 );
01073 struct ast_str *sql;
01074 char *char_args, varname[10];
01075 struct acf_odbc_query *query;
01076 struct ast_channel *chan;
01077 int i;
01078
01079 switch (cmd) {
01080 case CLI_INIT:
01081 e->command = "odbc read";
01082 e->usage =
01083 "Usage: odbc read <name> <args> [exec]\n"
01084 " Evaluates the SQL provided in the ODBC function <name>, and\n"
01085 " optionally executes the function. This function is intended for\n"
01086 " testing purposes. Remember to quote arguments containing spaces.\n";
01087 return NULL;
01088 case CLI_GENERATE:
01089 if (a->pos == 2) {
01090 int wordlen = strlen(a->word), which = 0;
01091
01092 AST_RWLIST_RDLOCK(&queries);
01093 AST_RWLIST_TRAVERSE(&queries, query, list) {
01094 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01095 if (++which > a->n) {
01096 char *res = ast_strdup(query->acf->name);
01097 AST_RWLIST_UNLOCK(&queries);
01098 return res;
01099 }
01100 }
01101 }
01102 AST_RWLIST_UNLOCK(&queries);
01103 return NULL;
01104 } else if (a->pos == 4) {
01105 return a->n == 0 ? ast_strdup("exec") : NULL;
01106 } else {
01107 return NULL;
01108 }
01109 }
01110
01111 if (a->argc < 4 || a->argc > 5) {
01112 return CLI_SHOWUSAGE;
01113 }
01114
01115 sql = ast_str_thread_get(&sql_buf, 16);
01116 if (!sql) {
01117 return CLI_FAILURE;
01118 }
01119
01120 AST_RWLIST_RDLOCK(&queries);
01121 AST_RWLIST_TRAVERSE(&queries, query, list) {
01122 if (!strcmp(query->acf->name, a->argv[2])) {
01123 break;
01124 }
01125 }
01126
01127 if (!query) {
01128 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01129 AST_RWLIST_UNLOCK(&queries);
01130 return CLI_SHOWUSAGE;
01131 }
01132
01133 if (ast_strlen_zero(query->sql_read)) {
01134 ast_cli(a->fd, "The function %s has no readsql parameter.\n", a->argv[2]);
01135 AST_RWLIST_UNLOCK(&queries);
01136 return CLI_SUCCESS;
01137 }
01138
01139 ast_str_make_space(&sql, strlen(query->sql_read) * 2 + 300);
01140
01141
01142 char_args = ast_strdupa(a->argv[3]);
01143
01144 chan = ast_dummy_channel_alloc();
01145
01146 AST_STANDARD_APP_ARGS(args, char_args);
01147 for (i = 0; i < args.argc; i++) {
01148 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01149 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01150 }
01151
01152 ast_str_substitute_variables(&sql, 0, chan, query->sql_read);
01153 chan = ast_channel_release(chan);
01154
01155 if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
01156
01157 struct odbc_obj *obj = NULL;
01158 int dsn, executed = 0;
01159 SQLHSTMT stmt;
01160 int rows = 0, res, x;
01161 SQLSMALLINT colcount = 0, collength;
01162 SQLLEN indicator;
01163 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
01164 char colname[256];
01165 SQLULEN maxcol;
01166
01167 if (!coldata) {
01168 AST_RWLIST_UNLOCK(&queries);
01169 return CLI_SUCCESS;
01170 }
01171
01172 for (dsn = 0; dsn < 5; dsn++) {
01173 if (ast_strlen_zero(query->readhandle[dsn])) {
01174 continue;
01175 }
01176 ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
01177 if (!(obj = ast_odbc_request_obj(query->readhandle[dsn], 0))) {
01178 continue;
01179 }
01180
01181 ast_debug(1, "Got obj\n");
01182 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01183 ast_odbc_release_obj(obj);
01184 obj = NULL;
01185 continue;
01186 }
01187
01188 executed = 1;
01189
01190 res = SQLNumResultCols(stmt, &colcount);
01191 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01192 ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(sql));
01193 SQLCloseCursor(stmt);
01194 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
01195 ast_odbc_release_obj(obj);
01196 obj = NULL;
01197 AST_RWLIST_UNLOCK(&queries);
01198 return CLI_SUCCESS;
01199 }
01200
01201 res = SQLFetch(stmt);
01202 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01203 SQLCloseCursor(stmt);
01204 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01205 ast_odbc_release_obj(obj);
01206 obj = NULL;
01207 if (res == SQL_NO_DATA) {
01208 ast_cli(a->fd, "Returned %d rows. Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(sql));
01209 break;
01210 } else {
01211 ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(sql));
01212 }
01213 AST_RWLIST_UNLOCK(&queries);
01214 return CLI_SUCCESS;
01215 }
01216 for (;;) {
01217 for (x = 0; x < colcount; x++) {
01218 maxcol = 0;
01219
01220 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
01221 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
01222 snprintf(colname, sizeof(colname), "field%d", x);
01223 }
01224
01225 res = ast_odbc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
01226 if (indicator == SQL_NULL_DATA) {
01227 ast_str_set(&coldata, 0, "(nil)");
01228 res = SQL_SUCCESS;
01229 }
01230
01231 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01232 ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(sql));
01233 SQLCloseCursor(stmt);
01234 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01235 ast_odbc_release_obj(obj);
01236 obj = NULL;
01237 AST_RWLIST_UNLOCK(&queries);
01238 return CLI_SUCCESS;
01239 }
01240
01241 ast_cli(a->fd, "%-20.20s %s\n", colname, ast_str_buffer(coldata));
01242 }
01243 rows++;
01244
01245
01246 res = SQLFetch(stmt);
01247 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
01248 break;
01249 }
01250 ast_cli(a->fd, "%-20.20s %s\n", "----------", "----------");
01251 }
01252 SQLCloseCursor(stmt);
01253 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01254 ast_odbc_release_obj(obj);
01255 obj = NULL;
01256 ast_cli(a->fd, "Returned %d row%s. Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
01257 break;
01258 }
01259 if (obj) {
01260 ast_odbc_release_obj(obj);
01261 obj = NULL;
01262 }
01263
01264 if (!executed) {
01265 ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(sql));
01266 }
01267 } else {
01268 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01269 }
01270 AST_RWLIST_UNLOCK(&queries);
01271 return CLI_SUCCESS;
01272 }
01273
01274 static char *cli_odbc_write(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01275 {
01276 AST_DECLARE_APP_ARGS(values,
01277 AST_APP_ARG(field)[100];
01278 );
01279 AST_DECLARE_APP_ARGS(args,
01280 AST_APP_ARG(field)[100];
01281 );
01282 struct ast_str *sql;
01283 char *char_args, *char_values, varname[10];
01284 struct acf_odbc_query *query;
01285 struct ast_channel *chan;
01286 int i;
01287
01288 switch (cmd) {
01289 case CLI_INIT:
01290 e->command = "odbc write";
01291 e->usage =
01292 "Usage: odbc write <name> <args> <value> [exec]\n"
01293 " Evaluates the SQL provided in the ODBC function <name>, and\n"
01294 " optionally executes the function. This function is intended for\n"
01295 " testing purposes. Remember to quote arguments containing spaces.\n";
01296 return NULL;
01297 case CLI_GENERATE:
01298 if (a->pos == 2) {
01299 int wordlen = strlen(a->word), which = 0;
01300
01301 AST_RWLIST_RDLOCK(&queries);
01302 AST_RWLIST_TRAVERSE(&queries, query, list) {
01303 if (!strncasecmp(query->acf->name, a->word, wordlen)) {
01304 if (++which > a->n) {
01305 char *res = ast_strdup(query->acf->name);
01306 AST_RWLIST_UNLOCK(&queries);
01307 return res;
01308 }
01309 }
01310 }
01311 AST_RWLIST_UNLOCK(&queries);
01312 return NULL;
01313 } else if (a->pos == 5) {
01314 return a->n == 0 ? ast_strdup("exec") : NULL;
01315 } else {
01316 return NULL;
01317 }
01318 }
01319
01320 if (a->argc < 5 || a->argc > 6) {
01321 return CLI_SHOWUSAGE;
01322 }
01323
01324 sql = ast_str_thread_get(&sql_buf, 16);
01325 if (!sql) {
01326 return CLI_FAILURE;
01327 }
01328
01329 AST_RWLIST_RDLOCK(&queries);
01330 AST_RWLIST_TRAVERSE(&queries, query, list) {
01331 if (!strcmp(query->acf->name, a->argv[2])) {
01332 break;
01333 }
01334 }
01335
01336 if (!query) {
01337 ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
01338 AST_RWLIST_UNLOCK(&queries);
01339 return CLI_SHOWUSAGE;
01340 }
01341
01342 if (ast_strlen_zero(query->sql_write)) {
01343 ast_cli(a->fd, "The function %s has no writesql parameter.\n", a->argv[2]);
01344 AST_RWLIST_UNLOCK(&queries);
01345 return CLI_SUCCESS;
01346 }
01347
01348 ast_str_make_space(&sql, strlen(query->sql_write) * 2 + 300);
01349
01350
01351 char_args = ast_strdupa(a->argv[3]);
01352 char_values = ast_strdupa(a->argv[4]);
01353
01354 chan = ast_dummy_channel_alloc();
01355
01356 AST_STANDARD_APP_ARGS(args, char_args);
01357 for (i = 0; i < args.argc; i++) {
01358 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
01359 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
01360 }
01361
01362
01363 AST_STANDARD_APP_ARGS(values, char_values);
01364 for (i = 0; i < values.argc; i++) {
01365 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
01366 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
01367 }
01368
01369
01370 pbx_builtin_pushvar_helper(chan, "VALUE", S_OR(a->argv[4], ""));
01371 ast_str_substitute_variables(&sql, 0, chan, query->sql_write);
01372 ast_debug(1, "SQL is %s\n", ast_str_buffer(sql));
01373 chan = ast_channel_release(chan);
01374
01375 if (a->argc == 6 && !strcmp(a->argv[5], "exec")) {
01376
01377 struct odbc_obj *obj = NULL;
01378 int dsn, executed = 0;
01379 SQLHSTMT stmt;
01380 SQLLEN rows = -1;
01381
01382 for (dsn = 0; dsn < 5; dsn++) {
01383 if (ast_strlen_zero(query->writehandle[dsn])) {
01384 continue;
01385 }
01386 if (!(obj = ast_odbc_request_obj(query->writehandle[dsn], 0))) {
01387 continue;
01388 }
01389 if (!(stmt = ast_odbc_direct_execute(obj, generic_execute, ast_str_buffer(sql)))) {
01390 ast_odbc_release_obj(obj);
01391 obj = NULL;
01392 continue;
01393 }
01394
01395 SQLRowCount(stmt, &rows);
01396 SQLCloseCursor(stmt);
01397 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
01398 ast_odbc_release_obj(obj);
01399 obj = NULL;
01400 ast_cli(a->fd, "Affected %d rows. Query executed on handle %d [%s]\n", (int)rows, dsn, query->writehandle[dsn]);
01401 executed = 1;
01402 break;
01403 }
01404
01405 if (!executed) {
01406 ast_cli(a->fd, "Failed to execute query.\n");
01407 }
01408 } else {
01409 ast_cli(a->fd, "%s\n", ast_str_buffer(sql));
01410 }
01411 AST_RWLIST_UNLOCK(&queries);
01412 return CLI_SUCCESS;
01413 }
01414
01415 static struct ast_cli_entry cli_func_odbc[] = {
01416 AST_CLI_DEFINE(cli_odbc_write, "Test setting a func_odbc function"),
01417 AST_CLI_DEFINE(cli_odbc_read, "Test reading a func_odbc function"),
01418 };
01419
01420 static int load_module(void)
01421 {
01422 int res = 0;
01423 struct ast_config *cfg;
01424 char *catg;
01425 struct ast_flags config_flags = { 0 };
01426
01427 res |= ast_custom_function_register(&fetch_function);
01428 res |= ast_register_application_xml(app_odbcfinish, exec_odbcfinish);
01429 AST_RWLIST_WRLOCK(&queries);
01430
01431 cfg = ast_config_load(config, config_flags);
01432 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
01433 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
01434 AST_RWLIST_UNLOCK(&queries);
01435 return AST_MODULE_LOAD_DECLINE;
01436 }
01437
01438 for (catg = ast_category_browse(cfg, NULL);
01439 catg;
01440 catg = ast_category_browse(cfg, catg)) {
01441 struct acf_odbc_query *query = NULL;
01442 int err;
01443
01444 if ((err = init_acf_query(cfg, catg, &query))) {
01445 if (err == ENOMEM)
01446 ast_log(LOG_ERROR, "Out of memory\n");
01447 else if (err == EINVAL)
01448 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
01449 else
01450 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
01451 } else {
01452 AST_RWLIST_INSERT_HEAD(&queries, query, list);
01453 ast_custom_function_register(query->acf);
01454 }
01455 }
01456
01457 ast_config_destroy(cfg);
01458 res |= ast_custom_function_register(&escape_function);
01459 ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01460
01461 AST_RWLIST_UNLOCK(&queries);
01462 return res;
01463 }
01464
01465 static int unload_module(void)
01466 {
01467 struct acf_odbc_query *query;
01468 int res = 0;
01469
01470 AST_RWLIST_WRLOCK(&queries);
01471 while (!AST_RWLIST_EMPTY(&queries)) {
01472 query = AST_RWLIST_REMOVE_HEAD(&queries, list);
01473 ast_custom_function_unregister(query->acf);
01474 free_acf_query(query);
01475 }
01476
01477 res |= ast_custom_function_unregister(&escape_function);
01478 res |= ast_custom_function_unregister(&fetch_function);
01479 res |= ast_unregister_application(app_odbcfinish);
01480 ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
01481
01482
01483 AST_RWLIST_UNLOCK(&queries);
01484 usleep(1);
01485 AST_RWLIST_WRLOCK(&queries);
01486
01487 AST_RWLIST_UNLOCK(&queries);
01488 return 0;
01489 }
01490
01491 static int reload(void)
01492 {
01493 int res = 0;
01494 struct ast_config *cfg;
01495 struct acf_odbc_query *oldquery;
01496 char *catg;
01497 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
01498
01499 cfg = ast_config_load(config, config_flags);
01500 if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
01501 return 0;
01502
01503 AST_RWLIST_WRLOCK(&queries);
01504
01505 while (!AST_RWLIST_EMPTY(&queries)) {
01506 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
01507 ast_custom_function_unregister(oldquery->acf);
01508 free_acf_query(oldquery);
01509 }
01510
01511 if (!cfg) {
01512 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
01513 goto reload_out;
01514 }
01515
01516 for (catg = ast_category_browse(cfg, NULL);
01517 catg;
01518 catg = ast_category_browse(cfg, catg)) {
01519 struct acf_odbc_query *query = NULL;
01520
01521 if (init_acf_query(cfg, catg, &query)) {
01522 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
01523 } else {
01524 AST_RWLIST_INSERT_HEAD(&queries, query, list);
01525 ast_custom_function_register(query->acf);
01526 }
01527 }
01528
01529 ast_config_destroy(cfg);
01530 reload_out:
01531 AST_RWLIST_UNLOCK(&queries);
01532 return res;
01533 }
01534
01535
01536
01537 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
01538 .load = load_module,
01539 .unload = unload_module,
01540 .reload = reload,
01541 );
01542