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