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