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: 211569 $")
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
00047 static char *config = "func_odbc.conf";
00048
00049 enum {
00050 OPT_ESCAPECOMMAS = (1 << 0),
00051 OPT_MULTIROW = (1 << 1),
00052 } odbc_option_flags;
00053
00054 struct acf_odbc_query {
00055 AST_RWLIST_ENTRY(acf_odbc_query) list;
00056 char readhandle[5][30];
00057 char writehandle[5][30];
00058 char sql_read[2048];
00059 char sql_write[2048];
00060 unsigned int flags;
00061 int rowlimit;
00062 struct ast_custom_function *acf;
00063 };
00064
00065 static void odbc_datastore_free(void *data);
00066
00067 struct ast_datastore_info odbc_info = {
00068 .type = "FUNC_ODBC",
00069 .destroy = odbc_datastore_free,
00070 };
00071
00072
00073 struct odbc_datastore_row {
00074 AST_LIST_ENTRY(odbc_datastore_row) list;
00075 char data[0];
00076 };
00077
00078
00079 struct odbc_datastore {
00080 AST_LIST_HEAD(, odbc_datastore_row);
00081 char names[0];
00082 };
00083
00084 AST_RWLIST_HEAD_STATIC(queries, acf_odbc_query);
00085
00086 static int resultcount = 0;
00087
00088 AST_THREADSTORAGE(coldata_buf);
00089 AST_THREADSTORAGE(colnames_buf);
00090
00091 static void odbc_datastore_free(void *data)
00092 {
00093 struct odbc_datastore *result = data;
00094 struct odbc_datastore_row *row;
00095 AST_LIST_LOCK(result);
00096 while ((row = AST_LIST_REMOVE_HEAD(result, list))) {
00097 ast_free(row);
00098 }
00099 AST_LIST_UNLOCK(result);
00100 AST_LIST_HEAD_DESTROY(result);
00101 ast_free(result);
00102 }
00103
00104 static SQLHSTMT generic_execute(struct odbc_obj *obj, void *data)
00105 {
00106 int res;
00107 char *sql = data;
00108 SQLHSTMT stmt;
00109
00110 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00111 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00112 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00113 return NULL;
00114 }
00115
00116 res = SQLExecDirect(stmt, (unsigned char *)sql, SQL_NTS);
00117 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00118 ast_log(LOG_WARNING, "SQL Exec Direct failed![%s]\n", sql);
00119 SQLCloseCursor(stmt);
00120 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00121 return NULL;
00122 }
00123
00124 return stmt;
00125 }
00126
00127
00128
00129
00130 static int acf_odbc_write(struct ast_channel *chan, const char *cmd, char *s, const char *value)
00131 {
00132 struct odbc_obj *obj = NULL;
00133 struct acf_odbc_query *query;
00134 char *t, varname[15];
00135 int i, dsn, bogus_chan = 0;
00136 AST_DECLARE_APP_ARGS(values,
00137 AST_APP_ARG(field)[100];
00138 );
00139 AST_DECLARE_APP_ARGS(args,
00140 AST_APP_ARG(field)[100];
00141 );
00142 SQLHSTMT stmt = NULL;
00143 SQLLEN rows=0;
00144 struct ast_str *buf = ast_str_create(16);
00145
00146 if (!buf) {
00147 return -1;
00148 }
00149
00150 AST_RWLIST_RDLOCK(&queries);
00151 AST_RWLIST_TRAVERSE(&queries, query, list) {
00152 if (!strcmp(query->acf->name, cmd)) {
00153 break;
00154 }
00155 }
00156
00157 if (!query) {
00158 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00159 AST_RWLIST_UNLOCK(&queries);
00160 ast_free(buf);
00161 return -1;
00162 }
00163
00164 if (!chan) {
00165 if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00166 bogus_chan = 1;
00167 }
00168
00169 if (chan)
00170 ast_autoservice_start(chan);
00171
00172 ast_str_make_space(&buf, strlen(query->sql_write) * 2);
00173
00174
00175 t = value ? ast_strdupa(value) : "";
00176
00177 if (!s || !t) {
00178 ast_log(LOG_ERROR, "Out of memory\n");
00179 AST_RWLIST_UNLOCK(&queries);
00180 if (chan)
00181 ast_autoservice_stop(chan);
00182 if (bogus_chan)
00183 ast_channel_free(chan);
00184 ast_free(buf);
00185 return -1;
00186 }
00187
00188 AST_STANDARD_APP_ARGS(args, s);
00189 for (i = 0; i < args.argc; i++) {
00190 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00191 pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
00192 }
00193
00194
00195 AST_STANDARD_APP_ARGS(values, t);
00196 for (i = 0; i < values.argc; i++) {
00197 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00198 pbx_builtin_pushvar_helper(chan, varname, values.field[i]);
00199 }
00200
00201
00202 pbx_builtin_pushvar_helper(chan, "VALUE", value ? value : "");
00203
00204 pbx_substitute_variables_helper(chan, query->sql_write, buf->str, buf->len - 1);
00205
00206
00207 for (i = 0; i < args.argc; i++) {
00208 snprintf(varname, sizeof(varname), "ARG%d", i + 1);
00209 pbx_builtin_setvar_helper(chan, varname, NULL);
00210 }
00211
00212 for (i = 0; i < values.argc; i++) {
00213 snprintf(varname, sizeof(varname), "VAL%d", i + 1);
00214 pbx_builtin_setvar_helper(chan, varname, NULL);
00215 }
00216 pbx_builtin_setvar_helper(chan, "VALUE", NULL);
00217
00218 AST_RWLIST_UNLOCK(&queries);
00219
00220 for (dsn = 0; dsn < 5; dsn++) {
00221 if (!ast_strlen_zero(query->writehandle[dsn])) {
00222 obj = ast_odbc_request_obj(query->writehandle[dsn], 0);
00223 if (obj)
00224 stmt = ast_odbc_direct_execute(obj, generic_execute, buf->str);
00225 }
00226 if (stmt)
00227 break;
00228 }
00229
00230 if (stmt) {
00231
00232 SQLRowCount(stmt, &rows);
00233 }
00234
00235
00236
00237
00238
00239 snprintf(varname, sizeof(varname), "%d", (int)rows);
00240 pbx_builtin_setvar_helper(chan, "ODBCROWS", varname);
00241
00242 if (stmt) {
00243 SQLCloseCursor(stmt);
00244 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00245 }
00246 if (obj)
00247 ast_odbc_release_obj(obj);
00248
00249 if (chan)
00250 ast_autoservice_stop(chan);
00251 if (bogus_chan)
00252 ast_channel_free(chan);
00253 ast_free(buf);
00254
00255 return 0;
00256 }
00257
00258 static int acf_odbc_read(struct ast_channel *chan, const char *cmd, char *s, char *buf, size_t len)
00259 {
00260 struct odbc_obj *obj = NULL;
00261 struct acf_odbc_query *query;
00262 char varname[15], rowcount[12] = "-1";
00263 struct ast_str *colnames = ast_str_thread_get(&colnames_buf, 16);
00264 int res, x, y, buflen = 0, escapecommas, rowlimit = 1, dsn, bogus_chan = 0;
00265 AST_DECLARE_APP_ARGS(args,
00266 AST_APP_ARG(field)[100];
00267 );
00268 SQLHSTMT stmt = NULL;
00269 SQLSMALLINT colcount=0;
00270 SQLLEN indicator;
00271 SQLSMALLINT collength;
00272 struct odbc_datastore *resultset = NULL;
00273 struct odbc_datastore_row *row = NULL;
00274 struct ast_str *sql = ast_str_create(16);
00275
00276 if (!sql) {
00277 return -1;
00278 }
00279
00280 ast_str_reset(colnames);
00281
00282 AST_RWLIST_RDLOCK(&queries);
00283 AST_RWLIST_TRAVERSE(&queries, query, list) {
00284 if (!strcmp(query->acf->name, cmd)) {
00285 break;
00286 }
00287 }
00288
00289 if (!query) {
00290 ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
00291 AST_RWLIST_UNLOCK(&queries);
00292 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00293 ast_free(sql);
00294 return -1;
00295 }
00296
00297 if (!chan) {
00298 if ((chan = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/func_odbc")))
00299 bogus_chan = 1;
00300 }
00301
00302 if (chan)
00303 ast_autoservice_start(chan);
00304
00305 AST_STANDARD_APP_ARGS(args, s);
00306 for (x = 0; x < args.argc; x++) {
00307 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00308 pbx_builtin_pushvar_helper(chan, varname, args.field[x]);
00309 }
00310
00311 ast_str_make_space(&sql, strlen(query->sql_read) * 2);
00312 pbx_substitute_variables_helper(chan, query->sql_read, sql->str, sql->len - 1);
00313
00314
00315 for (x = 0; x < args.argc; x++) {
00316 snprintf(varname, sizeof(varname), "ARG%d", x + 1);
00317 pbx_builtin_setvar_helper(chan, varname, NULL);
00318 }
00319
00320
00321 escapecommas = ast_test_flag(query, OPT_ESCAPECOMMAS);
00322 if (ast_test_flag(query, OPT_MULTIROW)) {
00323 resultset = ast_calloc(1, sizeof(*resultset));
00324 AST_LIST_HEAD_INIT(resultset);
00325 if (query->rowlimit)
00326 rowlimit = query->rowlimit;
00327 else
00328 rowlimit = INT_MAX;
00329 }
00330 AST_RWLIST_UNLOCK(&queries);
00331
00332 for (dsn = 0; dsn < 5; dsn++) {
00333 if (!ast_strlen_zero(query->readhandle[dsn])) {
00334 obj = ast_odbc_request_obj(query->readhandle[dsn], 0);
00335 if (obj)
00336 stmt = ast_odbc_direct_execute(obj, generic_execute, sql->str);
00337 }
00338 if (stmt)
00339 break;
00340 }
00341
00342 if (!stmt) {
00343 ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql->str);
00344 if (obj)
00345 ast_odbc_release_obj(obj);
00346 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00347 if (chan)
00348 ast_autoservice_stop(chan);
00349 if (bogus_chan)
00350 ast_channel_free(chan);
00351 ast_free(sql);
00352 return -1;
00353 }
00354
00355 res = SQLNumResultCols(stmt, &colcount);
00356 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00357 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql->str);
00358 SQLCloseCursor(stmt);
00359 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00360 ast_odbc_release_obj(obj);
00361 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00362 if (chan)
00363 ast_autoservice_stop(chan);
00364 if (bogus_chan)
00365 ast_channel_free(chan);
00366 ast_free(sql);
00367 return -1;
00368 }
00369
00370 res = SQLFetch(stmt);
00371 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00372 int res1 = -1;
00373 if (res == SQL_NO_DATA) {
00374 ast_verb(4, "Found no rows [%s]\n", sql->str);
00375 res1 = 0;
00376 buf[0] = '\0';
00377 ast_copy_string(rowcount, "0", sizeof(rowcount));
00378 } else {
00379 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
00380 }
00381 SQLCloseCursor(stmt);
00382 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00383 ast_odbc_release_obj(obj);
00384 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00385 if (chan)
00386 ast_autoservice_stop(chan);
00387 if (bogus_chan)
00388 ast_channel_free(chan);
00389 ast_free(sql);
00390 return res1;
00391 }
00392
00393 for (y = 0; y < rowlimit; y++) {
00394 buf[0] = '\0';
00395 for (x = 0; x < colcount; x++) {
00396 int i;
00397 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00398
00399 if (y == 0) {
00400 char colname[256];
00401 SQLULEN maxcol;
00402
00403 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00404 ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00405 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00406 snprintf(colname, sizeof(colname), "field%d", x);
00407 }
00408
00409 if (coldata->len < maxcol + 1) {
00410 ast_str_make_space(&coldata, maxcol + 1);
00411 }
00412
00413 if (colnames->used) {
00414 ast_str_append(&colnames, 0, ",");
00415 }
00416 ast_str_make_space(&colnames, strlen(colname) * 2 + 1 + colnames->used);
00417
00418
00419 for (i = 0; i < sizeof(colname); i++) {
00420 if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
00421 colnames->str[colnames->used++] = '\\';
00422 }
00423 colnames->str[colnames->used++] = colname[i];
00424
00425 if (colname[i] == '\0') {
00426 colnames->used--;
00427 break;
00428 }
00429 }
00430
00431 if (resultset) {
00432 void *tmp = ast_realloc(resultset, sizeof(*resultset) + colnames->used + 1);
00433 if (!tmp) {
00434 ast_log(LOG_ERROR, "No space for a new resultset?\n");
00435 ast_free(resultset);
00436 SQLCloseCursor(stmt);
00437 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00438 ast_odbc_release_obj(obj);
00439 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00440 if (chan)
00441 ast_autoservice_stop(chan);
00442 if (bogus_chan)
00443 ast_channel_free(chan);
00444 ast_free(sql);
00445 return -1;
00446 }
00447 resultset = tmp;
00448 strcpy((char *)resultset + sizeof(*resultset), colnames->str);
00449 }
00450 }
00451
00452 buflen = strlen(buf);
00453 res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata->str, coldata->len, &indicator);
00454 if (indicator == SQL_NULL_DATA) {
00455 ast_debug(3, "Got NULL data\n");
00456 ast_str_reset(coldata);
00457 res = SQL_SUCCESS;
00458 }
00459
00460 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00461 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql->str);
00462 y = -1;
00463 buf[0] = '\0';
00464 goto end_acf_read;
00465 }
00466
00467 ast_debug(2, "Got coldata of '%s'\n", coldata->str);
00468 coldata->used = strlen(coldata->str);
00469
00470
00471 for (i = 0; i < coldata->used; i++) {
00472 if (escapecommas && (coldata->str[i] == '\\' || coldata->str[i] == ',')) {
00473 buf[buflen++] = '\\';
00474 }
00475 buf[buflen++] = coldata->str[i];
00476
00477 if (buflen >= len - 2)
00478 break;
00479
00480 if (coldata->str[i] == '\0')
00481 break;
00482 }
00483
00484 buf[buflen++] = ',';
00485 buf[buflen] = '\0';
00486 ast_debug(2, "buf is now set to '%s'\n", buf);
00487 }
00488
00489 buf[buflen - 1] = '\0';
00490 ast_debug(2, "buf is now set to '%s'\n", buf);
00491
00492 if (resultset) {
00493 row = ast_calloc(1, sizeof(*row) + buflen);
00494 if (!row) {
00495 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00496 goto end_acf_read;
00497 }
00498 strcpy((char *)row + sizeof(*row), buf);
00499 AST_LIST_INSERT_TAIL(resultset, row, list);
00500
00501
00502 res = SQLFetch(stmt);
00503 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00504 if (res != SQL_NO_DATA)
00505 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
00506 y++;
00507 break;
00508 }
00509 }
00510 }
00511
00512 end_acf_read:
00513 snprintf(rowcount, sizeof(rowcount), "%d", y);
00514 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00515 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames->str);
00516 if (resultset) {
00517 int uid;
00518 struct ast_datastore *odbc_store;
00519 uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00520 snprintf(buf, len, "%d", uid);
00521 odbc_store = ast_datastore_alloc(&odbc_info, buf);
00522 if (!odbc_store) {
00523 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
00524 odbc_datastore_free(resultset);
00525 SQLCloseCursor(stmt);
00526 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00527 ast_odbc_release_obj(obj);
00528 if (chan)
00529 ast_autoservice_stop(chan);
00530 if (bogus_chan)
00531 ast_channel_free(chan);
00532 ast_free(sql);
00533 return -1;
00534 }
00535 odbc_store->data = resultset;
00536 ast_channel_datastore_add(chan, odbc_store);
00537 }
00538 SQLCloseCursor(stmt);
00539 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00540 ast_odbc_release_obj(obj);
00541 if (chan)
00542 ast_autoservice_stop(chan);
00543 if (bogus_chan)
00544 ast_channel_free(chan);
00545 ast_free(sql);
00546 return 0;
00547 }
00548
00549 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00550 {
00551 char *out = buf;
00552
00553 for (; *data && out - buf < len; data++) {
00554 if (*data == '\'') {
00555 *out = '\'';
00556 out++;
00557 }
00558 *out++ = *data;
00559 }
00560 *out = '\0';
00561
00562 return 0;
00563 }
00564
00565 static struct ast_custom_function escape_function = {
00566 .name = "SQL_ESC",
00567 .synopsis = "Escapes single ticks for use in SQL statements",
00568 .syntax = "SQL_ESC(<string>)",
00569 .desc =
00570 "Used in SQL templates to escape data which may contain single ticks (') which\n"
00571 "are otherwise used to delimit data. For example:\n"
00572 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
00573 .read = acf_escape,
00574 .write = NULL,
00575 };
00576
00577 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00578 {
00579 struct ast_datastore *store;
00580 struct odbc_datastore *resultset;
00581 struct odbc_datastore_row *row;
00582 store = ast_channel_datastore_find(chan, &odbc_info, data);
00583 if (!store) {
00584 return -1;
00585 }
00586 resultset = store->data;
00587 AST_LIST_LOCK(resultset);
00588 row = AST_LIST_REMOVE_HEAD(resultset, list);
00589 AST_LIST_UNLOCK(resultset);
00590 if (!row) {
00591
00592 ast_channel_datastore_remove(chan, store);
00593 ast_datastore_free(store);
00594 return -1;
00595 }
00596 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00597 ast_copy_string(buf, row->data, len);
00598 ast_free(row);
00599 return 0;
00600 }
00601
00602 static struct ast_custom_function fetch_function = {
00603 .name = "ODBC_FETCH",
00604 .synopsis = "Fetch a row from a multirow query",
00605 .syntax = "ODBC_FETCH(<result-id>)",
00606 .desc =
00607 "For queries which are marked as mode=multirow, the original query returns a\n"
00608 "result-id from which results may be fetched. This function implements the\n"
00609 "actual fetch of the results.\n",
00610 .read = acf_fetch,
00611 .write = NULL,
00612 };
00613
00614 static char *app_odbcfinish = "ODBCFinish";
00615 static char *syn_odbcfinish = "Clear the resultset of a successful multirow query";
00616 static char *desc_odbcfinish =
00617 "ODBCFinish(<result-id>)\n"
00618 " Clears any remaining rows of the specified resultset\n";
00619
00620
00621 static int exec_odbcfinish(struct ast_channel *chan, void *data)
00622 {
00623 struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00624 if (!store)
00625 return 0;
00626 ast_channel_datastore_remove(chan, store);
00627 ast_datastore_free(store);
00628 return 0;
00629 }
00630
00631 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00632 {
00633 const char *tmp;
00634 int i;
00635 int res;
00636
00637 if (!cfg || !catg) {
00638 return EINVAL;
00639 }
00640
00641 *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00642 if (! (*query))
00643 return ENOMEM;
00644
00645 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00646 char *tmp2 = ast_strdupa(tmp);
00647 AST_DECLARE_APP_ARGS(writeconf,
00648 AST_APP_ARG(dsn)[5];
00649 );
00650 AST_STANDARD_APP_ARGS(writeconf, tmp2);
00651 for (i = 0; i < 5; i++) {
00652 if (!ast_strlen_zero(writeconf.dsn[i]))
00653 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00654 }
00655 }
00656
00657 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00658 char *tmp2 = ast_strdupa(tmp);
00659 AST_DECLARE_APP_ARGS(readconf,
00660 AST_APP_ARG(dsn)[5];
00661 );
00662 AST_STANDARD_APP_ARGS(readconf, tmp2);
00663 for (i = 0; i < 5; i++) {
00664 if (!ast_strlen_zero(readconf.dsn[i]))
00665 ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00666 }
00667 } else {
00668
00669 for (i = 0; i < 5; i++) {
00670 if (!ast_strlen_zero((*query)->writehandle[i]))
00671 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00672 }
00673 }
00674
00675 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00676 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00677 else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00678 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
00679 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00680 }
00681
00682 if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00683 ast_free(*query);
00684 *query = NULL;
00685 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00686 return EINVAL;
00687 }
00688
00689 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00690 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00691 else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00692 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
00693 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00694 }
00695
00696 if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00697 ast_free(*query);
00698 *query = NULL;
00699 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00700 return EINVAL;
00701 }
00702
00703
00704 ast_set_flag((*query), OPT_ESCAPECOMMAS);
00705 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00706 if (ast_false(tmp))
00707 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00708 }
00709
00710 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00711 if (strcasecmp(tmp, "multirow") == 0)
00712 ast_set_flag((*query), OPT_MULTIROW);
00713 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00714 sscanf(tmp, "%30d", &((*query)->rowlimit));
00715 }
00716
00717 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00718 if (! (*query)->acf) {
00719 ast_free(*query);
00720 *query = NULL;
00721 return ENOMEM;
00722 }
00723
00724 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00725 if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00726 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00727 }
00728 } else {
00729 if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00730 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00731 }
00732 }
00733
00734 if (!((*query)->acf->name)) {
00735 ast_free((*query)->acf);
00736 ast_free(*query);
00737 *query = NULL;
00738 return ENOMEM;
00739 }
00740
00741 if (asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name) < 0) {
00742 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00743 (*query)->acf->syntax = NULL;
00744 }
00745
00746 if (!((*query)->acf->syntax)) {
00747 ast_free((char *)(*query)->acf->name);
00748 ast_free((*query)->acf);
00749 ast_free(*query);
00750 *query = NULL;
00751 return ENOMEM;
00752 }
00753
00754 (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00755
00756 res = 0;
00757 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00758 res = asprintf((char **)&((*query)->acf->desc),
00759 "Runs the following query, as defined in func_odbc.conf, performing\n"
00760 "substitution of the arguments into the query as specified by ${ARG1},\n"
00761 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
00762 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00763 "\nRead:\n%s\n\nWrite:\n%s\n",
00764 (*query)->sql_read,
00765 (*query)->sql_write);
00766 } else if (!ast_strlen_zero((*query)->sql_read)) {
00767 res = asprintf((char **)&((*query)->acf->desc),
00768 "Runs the following query, as defined in func_odbc.conf, performing\n"
00769 "substitution of the arguments into the query as specified by ${ARG1},\n"
00770 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
00771 (*query)->sql_read);
00772 } else if (!ast_strlen_zero((*query)->sql_write)) {
00773 res = asprintf((char **)&((*query)->acf->desc),
00774 "Runs the following query, as defined in func_odbc.conf, performing\n"
00775 "substitution of the arguments into the query as specified by ${ARG1},\n"
00776 "${ARG2}, ... ${ARGn}. The values are provided either in whole as\n"
00777 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00778 "This function may only be set.\nSQL:\n%s\n",
00779 (*query)->sql_write);
00780 } else {
00781 ast_free((char *)(*query)->acf->syntax);
00782 ast_free((char *)(*query)->acf->name);
00783 ast_free((*query)->acf);
00784 ast_free(*query);
00785 ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute. Ignoring.\n", catg);
00786 return EINVAL;
00787 }
00788
00789 if (res < 0) {
00790 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00791 (*query)->acf->desc = NULL;
00792 }
00793
00794
00795 if (!((*query)->acf->desc)) {
00796 ast_free((char *)(*query)->acf->syntax);
00797 ast_free((char *)(*query)->acf->name);
00798 ast_free((*query)->acf);
00799 ast_free(*query);
00800 *query = NULL;
00801 return ENOMEM;
00802 }
00803
00804 if (ast_strlen_zero((*query)->sql_read)) {
00805 (*query)->acf->read = NULL;
00806 } else {
00807 (*query)->acf->read = acf_odbc_read;
00808 }
00809
00810 if (ast_strlen_zero((*query)->sql_write)) {
00811 (*query)->acf->write = NULL;
00812 } else {
00813 (*query)->acf->write = acf_odbc_write;
00814 }
00815
00816 return 0;
00817 }
00818
00819 static int free_acf_query(struct acf_odbc_query *query)
00820 {
00821 if (query) {
00822 if (query->acf) {
00823 if (query->acf->name)
00824 ast_free((char *)query->acf->name);
00825 if (query->acf->syntax)
00826 ast_free((char *)query->acf->syntax);
00827 if (query->acf->desc)
00828 ast_free((char *)query->acf->desc);
00829 ast_free(query->acf);
00830 }
00831 ast_free(query);
00832 }
00833 return 0;
00834 }
00835
00836 static int load_module(void)
00837 {
00838 int res = 0;
00839 struct ast_config *cfg;
00840 char *catg;
00841 struct ast_flags config_flags = { 0 };
00842
00843 res |= ast_custom_function_register(&fetch_function);
00844 res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
00845 AST_RWLIST_WRLOCK(&queries);
00846
00847 cfg = ast_config_load(config, config_flags);
00848 if (!cfg) {
00849 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
00850 AST_RWLIST_UNLOCK(&queries);
00851 return AST_MODULE_LOAD_DECLINE;
00852 }
00853
00854 for (catg = ast_category_browse(cfg, NULL);
00855 catg;
00856 catg = ast_category_browse(cfg, catg)) {
00857 struct acf_odbc_query *query = NULL;
00858 int err;
00859
00860 if ((err = init_acf_query(cfg, catg, &query))) {
00861 if (err == ENOMEM)
00862 ast_log(LOG_ERROR, "Out of memory\n");
00863 else if (err == EINVAL)
00864 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
00865 else
00866 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
00867 } else {
00868 AST_RWLIST_INSERT_HEAD(&queries, query, list);
00869 ast_custom_function_register(query->acf);
00870 }
00871 }
00872
00873 ast_config_destroy(cfg);
00874 res |= ast_custom_function_register(&escape_function);
00875
00876 AST_RWLIST_UNLOCK(&queries);
00877 return res;
00878 }
00879
00880 static int unload_module(void)
00881 {
00882 struct acf_odbc_query *query;
00883 int res = 0;
00884
00885 AST_RWLIST_WRLOCK(&queries);
00886 while (!AST_RWLIST_EMPTY(&queries)) {
00887 query = AST_RWLIST_REMOVE_HEAD(&queries, list);
00888 ast_custom_function_unregister(query->acf);
00889 free_acf_query(query);
00890 }
00891
00892 res |= ast_custom_function_unregister(&escape_function);
00893 res |= ast_custom_function_unregister(&fetch_function);
00894 res |= ast_unregister_application(app_odbcfinish);
00895
00896
00897 AST_RWLIST_UNLOCK(&queries);
00898 usleep(1);
00899 AST_RWLIST_WRLOCK(&queries);
00900
00901 AST_RWLIST_UNLOCK(&queries);
00902 return 0;
00903 }
00904
00905 static int reload(void)
00906 {
00907 int res = 0;
00908 struct ast_config *cfg;
00909 struct acf_odbc_query *oldquery;
00910 char *catg;
00911 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
00912
00913 cfg = ast_config_load(config, config_flags);
00914 if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00915 return 0;
00916
00917 AST_RWLIST_WRLOCK(&queries);
00918
00919 while (!AST_RWLIST_EMPTY(&queries)) {
00920 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
00921 ast_custom_function_unregister(oldquery->acf);
00922 free_acf_query(oldquery);
00923 }
00924
00925 if (!cfg) {
00926 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00927 goto reload_out;
00928 }
00929
00930 for (catg = ast_category_browse(cfg, NULL);
00931 catg;
00932 catg = ast_category_browse(cfg, catg)) {
00933 struct acf_odbc_query *query = NULL;
00934
00935 if (init_acf_query(cfg, catg, &query)) {
00936 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00937 } else {
00938 AST_RWLIST_INSERT_HEAD(&queries, query, list);
00939 ast_custom_function_register(query->acf);
00940 }
00941 }
00942
00943 ast_config_destroy(cfg);
00944 reload_out:
00945 AST_RWLIST_UNLOCK(&queries);
00946 return res;
00947 }
00948
00949
00950
00951 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
00952 .load = load_module,
00953 .unload = unload_module,
00954 .reload = reload,
00955 );
00956