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: 155396 $")
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 for (x = 0; x < colcount; x++) {
00395 int i;
00396 struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
00397
00398 if (y == 0) {
00399 char colname[256];
00400 SQLULEN maxcol;
00401
00402 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
00403 ast_debug(3, "Got collength of %d and maxcol of %d for column '%s' (offset %d)\n", (int)collength, (int)maxcol, colname, x);
00404 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
00405 snprintf(colname, sizeof(colname), "field%d", x);
00406 }
00407
00408 if (coldata->len < maxcol + 1) {
00409 ast_str_make_space(&coldata, maxcol + 1);
00410 }
00411
00412 if (colnames->used) {
00413 ast_str_append(&colnames, 0, ",");
00414 }
00415 ast_str_make_space(&colnames, strlen(colname) * 2 + 1 + colnames->used);
00416
00417
00418 for (i = 0; i < sizeof(colname); i++) {
00419 if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) {
00420 colnames->str[colnames->used++] = '\\';
00421 }
00422 colnames->str[colnames->used++] = colname[i];
00423
00424 if (colname[i] == '\0') {
00425 colnames->used--;
00426 break;
00427 }
00428 }
00429
00430 if (resultset) {
00431 void *tmp = ast_realloc(resultset, sizeof(*resultset) + colnames->used + 1);
00432 if (!tmp) {
00433 ast_log(LOG_ERROR, "No space for a new resultset?\n");
00434 ast_free(resultset);
00435 SQLCloseCursor(stmt);
00436 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00437 ast_odbc_release_obj(obj);
00438 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00439 if (chan)
00440 ast_autoservice_stop(chan);
00441 if (bogus_chan)
00442 ast_channel_free(chan);
00443 ast_free(sql);
00444 return -1;
00445 }
00446 resultset = tmp;
00447 strcpy((char *)resultset + sizeof(*resultset), colnames->str);
00448 }
00449 }
00450
00451 buflen = strlen(buf);
00452 res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata->str, coldata->len, &indicator);
00453 if (indicator == SQL_NULL_DATA) {
00454 ast_debug(3, "Got NULL data\n");
00455 ast_str_reset(coldata);
00456 res = SQL_SUCCESS;
00457 }
00458
00459 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00460 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql->str);
00461 y = -1;
00462 buf[0] = '\0';
00463 goto end_acf_read;
00464 }
00465
00466 ast_debug(2, "Got coldata of '%s'\n", coldata->str);
00467 coldata->used = strlen(coldata->str);
00468
00469
00470 for (i = 0; i < coldata->used; i++) {
00471 if (escapecommas && (coldata->str[i] == '\\' || coldata->str[i] == ',')) {
00472 buf[buflen++] = '\\';
00473 }
00474 buf[buflen++] = coldata->str[i];
00475
00476 if (buflen >= len - 2)
00477 break;
00478
00479 if (coldata->str[i] == '\0')
00480 break;
00481 }
00482
00483 buf[buflen++] = ',';
00484 buf[buflen] = '\0';
00485 ast_debug(2, "buf is now set to '%s'\n", buf);
00486 }
00487
00488 buf[buflen - 1] = '\0';
00489 ast_debug(2, "buf is now set to '%s'\n", buf);
00490
00491 if (resultset) {
00492 row = ast_calloc(1, sizeof(*row) + buflen);
00493 if (!row) {
00494 ast_log(LOG_ERROR, "Unable to allocate space for more rows in this resultset.\n");
00495 goto end_acf_read;
00496 }
00497 strcpy((char *)row + sizeof(*row), buf);
00498 AST_LIST_INSERT_TAIL(resultset, row, list);
00499
00500
00501 res = SQLFetch(stmt);
00502 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00503 if (res != SQL_NO_DATA)
00504 ast_log(LOG_WARNING, "Error %d in FETCH [%s]\n", res, sql->str);
00505 y++;
00506 break;
00507 }
00508 }
00509 }
00510
00511 end_acf_read:
00512 snprintf(rowcount, sizeof(rowcount), "%d", y);
00513 pbx_builtin_setvar_helper(chan, "ODBCROWS", rowcount);
00514 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames->str);
00515 if (resultset) {
00516 int uid;
00517 struct ast_datastore *odbc_store;
00518 uid = ast_atomic_fetchadd_int(&resultcount, +1) + 1;
00519 snprintf(buf, len, "%d", uid);
00520 odbc_store = ast_datastore_alloc(&odbc_info, buf);
00521 if (!odbc_store) {
00522 ast_log(LOG_ERROR, "Rows retrieved, but unable to store it in the channel. Results fail.\n");
00523 odbc_datastore_free(resultset);
00524 SQLCloseCursor(stmt);
00525 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00526 ast_odbc_release_obj(obj);
00527 if (chan)
00528 ast_autoservice_stop(chan);
00529 if (bogus_chan)
00530 ast_channel_free(chan);
00531 ast_free(sql);
00532 return -1;
00533 }
00534 odbc_store->data = resultset;
00535 ast_channel_datastore_add(chan, odbc_store);
00536 }
00537 SQLCloseCursor(stmt);
00538 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00539 ast_odbc_release_obj(obj);
00540 if (chan)
00541 ast_autoservice_stop(chan);
00542 if (bogus_chan)
00543 ast_channel_free(chan);
00544 ast_free(sql);
00545 return 0;
00546 }
00547
00548 static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00549 {
00550 char *out = buf;
00551
00552 for (; *data && out - buf < len; data++) {
00553 if (*data == '\'') {
00554 *out = '\'';
00555 out++;
00556 }
00557 *out++ = *data;
00558 }
00559 *out = '\0';
00560
00561 return 0;
00562 }
00563
00564 static struct ast_custom_function escape_function = {
00565 .name = "SQL_ESC",
00566 .synopsis = "Escapes single ticks for use in SQL statements",
00567 .syntax = "SQL_ESC(<string>)",
00568 .desc =
00569 "Used in SQL templates to escape data which may contain single ticks (') which\n"
00570 "are otherwise used to delimit data. For example:\n"
00571 "SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'\n",
00572 .read = acf_escape,
00573 .write = NULL,
00574 };
00575
00576 static int acf_fetch(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00577 {
00578 struct ast_datastore *store;
00579 struct odbc_datastore *resultset;
00580 struct odbc_datastore_row *row;
00581 store = ast_channel_datastore_find(chan, &odbc_info, data);
00582 if (!store) {
00583 return -1;
00584 }
00585 resultset = store->data;
00586 AST_LIST_LOCK(resultset);
00587 row = AST_LIST_REMOVE_HEAD(resultset, list);
00588 AST_LIST_UNLOCK(resultset);
00589 if (!row) {
00590
00591 ast_channel_datastore_remove(chan, store);
00592 ast_datastore_free(store);
00593 return -1;
00594 }
00595 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", resultset->names);
00596 ast_copy_string(buf, row->data, len);
00597 ast_free(row);
00598 return 0;
00599 }
00600
00601 static struct ast_custom_function fetch_function = {
00602 .name = "ODBC_FETCH",
00603 .synopsis = "Fetch a row from a multirow query",
00604 .syntax = "ODBC_FETCH(<result-id>)",
00605 .desc =
00606 "For queries which are marked as mode=multirow, the original query returns a\n"
00607 "result-id from which results may be fetched. This function implements the\n"
00608 "actual fetch of the results.\n",
00609 .read = acf_fetch,
00610 .write = NULL,
00611 };
00612
00613 static char *app_odbcfinish = "ODBCFinish";
00614 static char *syn_odbcfinish = "Clear the resultset of a successful multirow query";
00615 static char *desc_odbcfinish =
00616 "ODBCFinish(<result-id>)\n"
00617 " Clears any remaining rows of the specified resultset\n";
00618
00619
00620 static int exec_odbcfinish(struct ast_channel *chan, void *data)
00621 {
00622 struct ast_datastore *store = ast_channel_datastore_find(chan, &odbc_info, data);
00623 if (!store)
00624 return 0;
00625 ast_channel_datastore_remove(chan, store);
00626 ast_datastore_free(store);
00627 return 0;
00628 }
00629
00630 static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query)
00631 {
00632 const char *tmp;
00633 int i;
00634 int res;
00635
00636 if (!cfg || !catg) {
00637 return EINVAL;
00638 }
00639
00640 *query = ast_calloc(1, sizeof(struct acf_odbc_query));
00641 if (! (*query))
00642 return ENOMEM;
00643
00644 if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) {
00645 char *tmp2 = ast_strdupa(tmp);
00646 AST_DECLARE_APP_ARGS(writeconf,
00647 AST_APP_ARG(dsn)[5];
00648 );
00649 AST_STANDARD_APP_ARGS(writeconf, tmp2);
00650 for (i = 0; i < 5; i++) {
00651 if (!ast_strlen_zero(writeconf.dsn[i]))
00652 ast_copy_string((*query)->writehandle[i], writeconf.dsn[i], sizeof((*query)->writehandle[i]));
00653 }
00654 }
00655
00656 if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) {
00657 char *tmp2 = ast_strdupa(tmp);
00658 AST_DECLARE_APP_ARGS(readconf,
00659 AST_APP_ARG(dsn)[5];
00660 );
00661 AST_STANDARD_APP_ARGS(readconf, tmp2);
00662 for (i = 0; i < 5; i++) {
00663 if (!ast_strlen_zero(readconf.dsn[i]))
00664 ast_copy_string((*query)->readhandle[i], readconf.dsn[i], sizeof((*query)->readhandle[i]));
00665 }
00666 } else {
00667
00668 for (i = 0; i < 5; i++) {
00669 if (!ast_strlen_zero((*query)->writehandle[i]))
00670 ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i]));
00671 }
00672 }
00673
00674 if ((tmp = ast_variable_retrieve(cfg, catg, "readsql")))
00675 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00676 else if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) {
00677 ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s. Please use 'readsql' instead.\n", catg);
00678 ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read));
00679 }
00680
00681 if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) {
00682 ast_free(*query);
00683 *query = NULL;
00684 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg);
00685 return EINVAL;
00686 }
00687
00688 if ((tmp = ast_variable_retrieve(cfg, catg, "writesql")))
00689 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00690 else if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) {
00691 ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s. Please use 'writesql' instead.\n", catg);
00692 ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write));
00693 }
00694
00695 if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) {
00696 ast_free(*query);
00697 *query = NULL;
00698 ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg);
00699 return EINVAL;
00700 }
00701
00702
00703 ast_set_flag((*query), OPT_ESCAPECOMMAS);
00704 if ((tmp = ast_variable_retrieve(cfg, catg, "escapecommas"))) {
00705 if (ast_false(tmp))
00706 ast_clear_flag((*query), OPT_ESCAPECOMMAS);
00707 }
00708
00709 if ((tmp = ast_variable_retrieve(cfg, catg, "mode"))) {
00710 if (strcasecmp(tmp, "multirow") == 0)
00711 ast_set_flag((*query), OPT_MULTIROW);
00712 if ((tmp = ast_variable_retrieve(cfg, catg, "rowlimit")))
00713 sscanf(tmp, "%d", &((*query)->rowlimit));
00714 }
00715
00716 (*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function));
00717 if (! (*query)->acf) {
00718 ast_free(*query);
00719 *query = NULL;
00720 return ENOMEM;
00721 }
00722
00723 if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) {
00724 if (asprintf((char **)&((*query)->acf->name), "%s_%s", tmp, catg) < 0) {
00725 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00726 }
00727 } else {
00728 if (asprintf((char **)&((*query)->acf->name), "ODBC_%s", catg) < 0) {
00729 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00730 }
00731 }
00732
00733 if (!((*query)->acf->name)) {
00734 ast_free((*query)->acf);
00735 ast_free(*query);
00736 *query = NULL;
00737 return ENOMEM;
00738 }
00739
00740 if (asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name) < 0) {
00741 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00742 (*query)->acf->syntax = NULL;
00743 }
00744
00745 if (!((*query)->acf->syntax)) {
00746 ast_free((char *)(*query)->acf->name);
00747 ast_free((*query)->acf);
00748 ast_free(*query);
00749 *query = NULL;
00750 return ENOMEM;
00751 }
00752
00753 (*query)->acf->synopsis = "Runs the referenced query with the specified arguments";
00754
00755 res = 0;
00756 if (!ast_strlen_zero((*query)->sql_read) && !ast_strlen_zero((*query)->sql_write)) {
00757 res = asprintf((char **)&((*query)->acf->desc),
00758 "Runs the following query, as defined in func_odbc.conf, performing\n"
00759 "substitution of the arguments into the query as specified by ${ARG1},\n"
00760 "${ARG2}, ... ${ARGn}. When setting the function, the values are provided\n"
00761 "either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00762 "\nRead:\n%s\n\nWrite:\n%s\n",
00763 (*query)->sql_read,
00764 (*query)->sql_write);
00765 } else if (!ast_strlen_zero((*query)->sql_read)) {
00766 res = asprintf((char **)&((*query)->acf->desc),
00767 "Runs the following query, as defined in func_odbc.conf, performing\n"
00768 "substitution of the arguments into the query as specified by ${ARG1},\n"
00769 "${ARG2}, ... ${ARGn}. This function may only be read, not set.\n\nSQL:\n%s\n",
00770 (*query)->sql_read);
00771 } else if (!ast_strlen_zero((*query)->sql_write)) {
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}. The values are provided either in whole as\n"
00776 "${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
00777 "This function may only be set.\nSQL:\n%s\n",
00778 (*query)->sql_write);
00779 } else {
00780 ast_free((char *)(*query)->acf->syntax);
00781 ast_free((char *)(*query)->acf->name);
00782 ast_free((*query)->acf);
00783 ast_free(*query);
00784 ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute. Ignoring.\n", catg);
00785 return EINVAL;
00786 }
00787
00788 if (res < 0) {
00789 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00790 (*query)->acf->desc = NULL;
00791 }
00792
00793
00794 if (!((*query)->acf->desc)) {
00795 ast_free((char *)(*query)->acf->syntax);
00796 ast_free((char *)(*query)->acf->name);
00797 ast_free((*query)->acf);
00798 ast_free(*query);
00799 *query = NULL;
00800 return ENOMEM;
00801 }
00802
00803 if (ast_strlen_zero((*query)->sql_read)) {
00804 (*query)->acf->read = NULL;
00805 } else {
00806 (*query)->acf->read = acf_odbc_read;
00807 }
00808
00809 if (ast_strlen_zero((*query)->sql_write)) {
00810 (*query)->acf->write = NULL;
00811 } else {
00812 (*query)->acf->write = acf_odbc_write;
00813 }
00814
00815 return 0;
00816 }
00817
00818 static int free_acf_query(struct acf_odbc_query *query)
00819 {
00820 if (query) {
00821 if (query->acf) {
00822 if (query->acf->name)
00823 ast_free((char *)query->acf->name);
00824 if (query->acf->syntax)
00825 ast_free((char *)query->acf->syntax);
00826 if (query->acf->desc)
00827 ast_free((char *)query->acf->desc);
00828 ast_free(query->acf);
00829 }
00830 ast_free(query);
00831 }
00832 return 0;
00833 }
00834
00835 static int load_module(void)
00836 {
00837 int res = 0;
00838 struct ast_config *cfg;
00839 char *catg;
00840 struct ast_flags config_flags = { 0 };
00841
00842 res |= ast_custom_function_register(&fetch_function);
00843 res |= ast_register_application(app_odbcfinish, exec_odbcfinish, syn_odbcfinish, desc_odbcfinish);
00844 AST_RWLIST_WRLOCK(&queries);
00845
00846 cfg = ast_config_load(config, config_flags);
00847 if (!cfg) {
00848 ast_log(LOG_NOTICE, "Unable to load config for func_odbc: %s\n", config);
00849 AST_RWLIST_UNLOCK(&queries);
00850 return AST_MODULE_LOAD_DECLINE;
00851 }
00852
00853 for (catg = ast_category_browse(cfg, NULL);
00854 catg;
00855 catg = ast_category_browse(cfg, catg)) {
00856 struct acf_odbc_query *query = NULL;
00857 int err;
00858
00859 if ((err = init_acf_query(cfg, catg, &query))) {
00860 if (err == ENOMEM)
00861 ast_log(LOG_ERROR, "Out of memory\n");
00862 else if (err == EINVAL)
00863 ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
00864 else
00865 ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
00866 } else {
00867 AST_RWLIST_INSERT_HEAD(&queries, query, list);
00868 ast_custom_function_register(query->acf);
00869 }
00870 }
00871
00872 ast_config_destroy(cfg);
00873 res |= ast_custom_function_register(&escape_function);
00874
00875 AST_RWLIST_UNLOCK(&queries);
00876 return res;
00877 }
00878
00879 static int unload_module(void)
00880 {
00881 struct acf_odbc_query *query;
00882 int res = 0;
00883
00884 AST_RWLIST_WRLOCK(&queries);
00885 while (!AST_RWLIST_EMPTY(&queries)) {
00886 query = AST_RWLIST_REMOVE_HEAD(&queries, list);
00887 ast_custom_function_unregister(query->acf);
00888 free_acf_query(query);
00889 }
00890
00891 res |= ast_custom_function_unregister(&escape_function);
00892 res |= ast_custom_function_unregister(&fetch_function);
00893 res |= ast_unregister_application(app_odbcfinish);
00894
00895
00896 AST_RWLIST_UNLOCK(&queries);
00897 usleep(1);
00898 AST_RWLIST_WRLOCK(&queries);
00899
00900 AST_RWLIST_UNLOCK(&queries);
00901 return 0;
00902 }
00903
00904 static int reload(void)
00905 {
00906 int res = 0;
00907 struct ast_config *cfg;
00908 struct acf_odbc_query *oldquery;
00909 char *catg;
00910 struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
00911
00912 cfg = ast_config_load(config, config_flags);
00913 if (cfg == CONFIG_STATUS_FILEUNCHANGED)
00914 return 0;
00915
00916 AST_RWLIST_WRLOCK(&queries);
00917
00918 while (!AST_RWLIST_EMPTY(&queries)) {
00919 oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
00920 ast_custom_function_unregister(oldquery->acf);
00921 free_acf_query(oldquery);
00922 }
00923
00924 if (!cfg) {
00925 ast_log(LOG_WARNING, "Unable to load config for func_odbc: %s\n", config);
00926 goto reload_out;
00927 }
00928
00929 for (catg = ast_category_browse(cfg, NULL);
00930 catg;
00931 catg = ast_category_browse(cfg, catg)) {
00932 struct acf_odbc_query *query = NULL;
00933
00934 if (init_acf_query(cfg, catg, &query)) {
00935 ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
00936 } else {
00937 AST_RWLIST_INSERT_HEAD(&queries, query, list);
00938 ast_custom_function_register(query->acf);
00939 }
00940 }
00941
00942 ast_config_destroy(cfg);
00943 reload_out:
00944 AST_RWLIST_UNLOCK(&queries);
00945 return res;
00946 }
00947
00948
00949
00950 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups",
00951 .load = load_module,
00952 .unload = unload_module,
00953 .reload = reload,
00954 );
00955