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