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
00036
00037 #include "asterisk.h"
00038
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 310140 $")
00040
00041 #include <stdio.h>
00042 #include <stdlib.h>
00043 #include <unistd.h>
00044 #include <string.h>
00045
00046 #include "asterisk/file.h"
00047 #include "asterisk/logger.h"
00048 #include "asterisk/channel.h"
00049 #include "asterisk/pbx.h"
00050 #include "asterisk/config.h"
00051 #include "asterisk/module.h"
00052 #include "asterisk/lock.h"
00053 #include "asterisk/options.h"
00054 #include "asterisk/res_odbc.h"
00055 #include "asterisk/utils.h"
00056 #include "asterisk/stringfields.h"
00057
00058 struct custom_prepare_struct {
00059 const char *sql;
00060 const char *extra;
00061 AST_DECLARE_STRING_FIELDS(
00062 AST_STRING_FIELD(encoding)[256];
00063 );
00064 va_list ap;
00065 };
00066
00067 static void decode_chunk(char *chunk)
00068 {
00069 for (; *chunk; chunk++) {
00070 if (*chunk == '^' && strchr("0123456789ABCDEF", chunk[1]) && strchr("0123456789ABCDEF", chunk[2])) {
00071 sscanf(chunk + 1, "%02hhX", chunk);
00072 memmove(chunk + 1, chunk + 3, strlen(chunk + 3) + 1);
00073 }
00074 }
00075 }
00076
00077 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
00078 {
00079 int res, x = 1;
00080 struct custom_prepare_struct *cps = data;
00081 const char *newparam, *newval;
00082 char encodebuf[1024];
00083 SQLHSTMT stmt;
00084 va_list ap;
00085
00086 va_copy(ap, cps->ap);
00087
00088 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00089 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00090 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00091 return NULL;
00092 }
00093
00094 res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
00095 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00096 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
00097 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00098 return NULL;
00099 }
00100
00101 while ((newparam = va_arg(ap, const char *))) {
00102 newval = va_arg(ap, const char *);
00103 if (strchr(newval, ';') || strchr(newval, '^')) {
00104 char *eptr = encodebuf;
00105 const char *vptr = newval;
00106 for (; *vptr && eptr < encodebuf + sizeof(encodebuf); vptr++) {
00107 if (strchr("^;", *vptr)) {
00108
00109 snprintf(eptr, encodebuf + sizeof(encodebuf) - eptr, "^%02hhX", *vptr);
00110 eptr += 3;
00111 } else {
00112 *eptr++ = *vptr;
00113 }
00114 }
00115 if (eptr < encodebuf + sizeof(encodebuf)) {
00116 *eptr = '\0';
00117 } else {
00118 encodebuf[sizeof(encodebuf) - 1] = '\0';
00119 }
00120 ast_string_field_set(cps, encoding[x], encodebuf);
00121 newval = cps->encoding[x];
00122 }
00123 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
00124 }
00125 va_end(ap);
00126
00127 if (!ast_strlen_zero(cps->extra))
00128 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
00129 return stmt;
00130 }
00131
00132 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
00133 {
00134 struct odbc_obj *obj;
00135 SQLHSTMT stmt;
00136 char sql[1024];
00137 char coltitle[256];
00138 char rowdata[2048];
00139 char *op;
00140 const char *newparam, *newval;
00141 char *stringp;
00142 char *chunk;
00143 SQLSMALLINT collen;
00144 int res;
00145 int x;
00146 struct ast_variable *var=NULL, *prev=NULL;
00147 SQLULEN colsize;
00148 SQLSMALLINT colcount=0;
00149 SQLSMALLINT datatype;
00150 SQLSMALLINT decimaldigits;
00151 SQLSMALLINT nullable;
00152 SQLLEN indicator;
00153 va_list aq;
00154 struct custom_prepare_struct cps = { .sql = sql };
00155
00156 if (ast_string_field_init(&cps, 256)) {
00157 return NULL;
00158 }
00159 va_copy(cps.ap, ap);
00160 va_copy(aq, ap);
00161
00162 if (!table) {
00163 ast_string_field_free_memory(&cps);
00164 return NULL;
00165 }
00166
00167 obj = ast_odbc_request_obj(database, 0);
00168
00169 if (!obj) {
00170 ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
00171 ast_string_field_free_memory(&cps);
00172 return NULL;
00173 }
00174
00175 newparam = va_arg(aq, const char *);
00176 if (!newparam) {
00177 ast_odbc_release_obj(obj);
00178 ast_string_field_free_memory(&cps);
00179 return NULL;
00180 }
00181 newval = va_arg(aq, const char *);
00182 op = !strchr(newparam, ' ') ? " =" : "";
00183 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00184 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00185 while((newparam = va_arg(aq, const char *))) {
00186 op = !strchr(newparam, ' ') ? " =" : "";
00187 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00188 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00189 newval = va_arg(aq, const char *);
00190 }
00191 va_end(aq);
00192
00193 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00194
00195 if (!stmt) {
00196 ast_odbc_release_obj(obj);
00197 ast_string_field_free_memory(&cps);
00198 return NULL;
00199 }
00200
00201 res = SQLNumResultCols(stmt, &colcount);
00202 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00203 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00204 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00205 ast_odbc_release_obj(obj);
00206 ast_string_field_free_memory(&cps);
00207 return NULL;
00208 }
00209
00210 res = SQLFetch(stmt);
00211 if (res == SQL_NO_DATA) {
00212 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00213 ast_odbc_release_obj(obj);
00214 ast_string_field_free_memory(&cps);
00215 return NULL;
00216 }
00217 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00218 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00219 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00220 ast_odbc_release_obj(obj);
00221 ast_string_field_free_memory(&cps);
00222 return NULL;
00223 }
00224 for (x = 0; x < colcount; x++) {
00225 rowdata[0] = '\0';
00226 colsize = 0;
00227 collen = sizeof(coltitle);
00228 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
00229 &datatype, &colsize, &decimaldigits, &nullable);
00230 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00231 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00232 if (var)
00233 ast_variables_destroy(var);
00234 ast_odbc_release_obj(obj);
00235 ast_string_field_free_memory(&cps);
00236 return NULL;
00237 }
00238
00239 indicator = 0;
00240 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
00241 if (indicator == SQL_NULL_DATA)
00242 continue;
00243
00244 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00245 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00246 if (var)
00247 ast_variables_destroy(var);
00248 ast_odbc_release_obj(obj);
00249 return NULL;
00250 }
00251 stringp = rowdata;
00252 while (stringp) {
00253 chunk = strsep(&stringp, ";");
00254 if (!ast_strlen_zero(ast_strip(chunk))) {
00255 if (strchr(chunk, '^')) {
00256 decode_chunk(chunk);
00257 }
00258 if (prev) {
00259 prev->next = ast_variable_new(coltitle, chunk);
00260 if (prev->next) {
00261 prev = prev->next;
00262 }
00263 } else {
00264 prev = var = ast_variable_new(coltitle, chunk);
00265 }
00266 }
00267 }
00268 }
00269
00270
00271 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00272 ast_odbc_release_obj(obj);
00273 ast_string_field_free_memory(&cps);
00274 return var;
00275 }
00276
00277 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
00278 {
00279 struct odbc_obj *obj;
00280 SQLHSTMT stmt;
00281 char sql[1024];
00282 char coltitle[256];
00283 char rowdata[2048];
00284 const char *initfield=NULL;
00285 char *op;
00286 const char *newparam, *newval;
00287 char *stringp;
00288 char *chunk;
00289 SQLSMALLINT collen;
00290 int res;
00291 int x;
00292 struct ast_variable *var=NULL;
00293 struct ast_config *cfg=NULL;
00294 struct ast_category *cat=NULL;
00295 struct ast_realloca ra;
00296 SQLULEN colsize;
00297 SQLSMALLINT colcount=0;
00298 SQLSMALLINT datatype;
00299 SQLSMALLINT decimaldigits;
00300 SQLSMALLINT nullable;
00301 SQLLEN indicator;
00302 struct custom_prepare_struct cps = { .sql = sql };
00303 va_list aq;
00304
00305 if (!table || ast_string_field_init(&cps, 256)) {
00306 return NULL;
00307 }
00308 va_copy(cps.ap, ap);
00309 va_copy(aq, ap);
00310
00311 memset(&ra, 0, sizeof(ra));
00312
00313 obj = ast_odbc_request_obj(database, 0);
00314 if (!obj) {
00315 ast_string_field_free_memory(&cps);
00316 return NULL;
00317 }
00318
00319 newparam = va_arg(aq, const char *);
00320 if (!newparam) {
00321 ast_odbc_release_obj(obj);
00322 ast_string_field_free_memory(&cps);
00323 return NULL;
00324 }
00325 initfield = ast_strdupa(newparam);
00326 if ((op = strchr(initfield, ' ')))
00327 *op = '\0';
00328 newval = va_arg(aq, const char *);
00329 op = !strchr(newparam, ' ') ? " =" : "";
00330 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
00331 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00332 while((newparam = va_arg(aq, const char *))) {
00333 op = !strchr(newparam, ' ') ? " =" : "";
00334 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
00335 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
00336 newval = va_arg(aq, const char *);
00337 }
00338 if (initfield)
00339 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
00340 va_end(aq);
00341
00342 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00343
00344 if (!stmt) {
00345 ast_odbc_release_obj(obj);
00346 ast_string_field_free_memory(&cps);
00347 return NULL;
00348 }
00349
00350 res = SQLNumResultCols(stmt, &colcount);
00351 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00352 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
00353 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00354 ast_odbc_release_obj(obj);
00355 ast_string_field_free_memory(&cps);
00356 return NULL;
00357 }
00358
00359 cfg = ast_config_new();
00360 if (!cfg) {
00361 ast_log(LOG_WARNING, "Out of memory!\n");
00362 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00363 ast_odbc_release_obj(obj);
00364 ast_string_field_free_memory(&cps);
00365 return NULL;
00366 }
00367
00368 while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
00369 var = NULL;
00370 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00371 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
00372 continue;
00373 }
00374 cat = ast_category_new("");
00375 if (!cat) {
00376 ast_log(LOG_WARNING, "Out of memory!\n");
00377 continue;
00378 }
00379 for (x=0;x<colcount;x++) {
00380 rowdata[0] = '\0';
00381 colsize = 0;
00382 collen = sizeof(coltitle);
00383 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
00384 &datatype, &colsize, &decimaldigits, &nullable);
00385 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00386 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
00387 ast_category_destroy(cat);
00388 continue;
00389 }
00390
00391 indicator = 0;
00392 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
00393 if (indicator == SQL_NULL_DATA)
00394 continue;
00395
00396 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00397 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
00398 ast_category_destroy(cat);
00399 continue;
00400 }
00401 stringp = rowdata;
00402 while (stringp) {
00403 chunk = strsep(&stringp, ";");
00404 if (!ast_strlen_zero(ast_strip(chunk))) {
00405 if (strchr(chunk, '^')) {
00406 decode_chunk(chunk);
00407 }
00408 if (initfield && !strcmp(initfield, coltitle)) {
00409 ast_category_rename(cat, chunk);
00410 }
00411 var = ast_variable_new(coltitle, chunk);
00412 ast_variable_append(cat, var);
00413 }
00414 }
00415 }
00416 ast_category_append(cfg, cat);
00417 }
00418
00419 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00420 ast_odbc_release_obj(obj);
00421 ast_string_field_free_memory(&cps);
00422 return cfg;
00423 }
00424
00425 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
00426 {
00427 struct odbc_obj *obj;
00428 SQLHSTMT stmt;
00429 char sql[256];
00430 SQLLEN rowcount=0;
00431 const char *newparam, *newval;
00432 int res;
00433 va_list aq;
00434 struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
00435
00436 if (!table || ast_string_field_init(&cps, 256)) {
00437 return -1;
00438 }
00439 va_copy(cps.ap, ap);
00440 va_copy(aq, ap);
00441
00442 if (!(obj = ast_odbc_request_obj(database, 0))) {
00443 ast_string_field_free_memory(&cps);
00444 return -1;
00445 }
00446
00447 newparam = va_arg(aq, const char *);
00448 if (!newparam) {
00449 ast_odbc_release_obj(obj);
00450 ast_string_field_free_memory(&cps);
00451 return -1;
00452 }
00453 newval = va_arg(aq, const char *);
00454 snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
00455 while((newparam = va_arg(aq, const char *))) {
00456 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
00457 newval = va_arg(aq, const char *);
00458 }
00459 va_end(aq);
00460 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
00461
00462 stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
00463
00464 if (!stmt) {
00465 ast_odbc_release_obj(obj);
00466 ast_string_field_free_memory(&cps);
00467 return -1;
00468 }
00469
00470 res = SQLRowCount(stmt, &rowcount);
00471 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00472 ast_odbc_release_obj(obj);
00473 ast_string_field_free_memory(&cps);
00474
00475 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00476 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
00477 return -1;
00478 }
00479
00480 if (rowcount >= 0) {
00481 return (int) rowcount;
00482 }
00483
00484 return -1;
00485 }
00486
00487 struct config_odbc_obj {
00488 char *sql;
00489 unsigned long cat_metric;
00490 char category[128];
00491 char var_name[128];
00492 char var_val[1024];
00493 SQLLEN err;
00494 };
00495
00496 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
00497 {
00498 struct config_odbc_obj *q = data;
00499 SQLHSTMT sth;
00500 int res;
00501
00502 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
00503 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00504 if (option_verbose > 3)
00505 ast_verbose( VERBOSE_PREFIX_4 "Failure in AllocStatement %d\n", res);
00506 return NULL;
00507 }
00508
00509 res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
00510 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00511 if (option_verbose > 3)
00512 ast_verbose( VERBOSE_PREFIX_4 "Error in PREPARE %d\n", res);
00513 SQLFreeHandle(SQL_HANDLE_STMT, sth);
00514 return NULL;
00515 }
00516
00517 SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
00518 SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
00519 SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
00520 SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
00521
00522 return sth;
00523 }
00524
00525 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, int withcomments)
00526 {
00527 struct ast_variable *new_v;
00528 struct ast_category *cur_cat;
00529 int res = 0;
00530 struct odbc_obj *obj;
00531 char sqlbuf[1024] = "";
00532 char *sql = sqlbuf;
00533 size_t sqlleft = sizeof(sqlbuf);
00534 unsigned int last_cat_metric = 0;
00535 SQLSMALLINT rowcount = 0;
00536 SQLHSTMT stmt;
00537 char last[128] = "";
00538 struct config_odbc_obj q;
00539
00540 memset(&q, 0, sizeof(q));
00541
00542 if (!file || !strcmp (file, "res_config_odbc.conf"))
00543 return NULL;
00544
00545 obj = ast_odbc_request_obj(database, 0);
00546 if (!obj)
00547 return NULL;
00548
00549 ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
00550 ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
00551 ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
00552 q.sql = sqlbuf;
00553
00554 stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
00555
00556 if (!stmt) {
00557 ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
00558 ast_odbc_release_obj(obj);
00559 return NULL;
00560 }
00561
00562 res = SQLNumResultCols(stmt, &rowcount);
00563
00564 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00565 ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
00566 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00567 ast_odbc_release_obj(obj);
00568 return NULL;
00569 }
00570
00571 if (!rowcount) {
00572 ast_log(LOG_NOTICE, "found nothing\n");
00573 ast_odbc_release_obj(obj);
00574 return cfg;
00575 }
00576
00577 cur_cat = ast_config_get_current_category(cfg);
00578
00579 while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
00580 if (!strcmp (q.var_name, "#include")) {
00581 if (!ast_config_internal_load(q.var_val, cfg, 0)) {
00582 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00583 ast_odbc_release_obj(obj);
00584 return NULL;
00585 }
00586 continue;
00587 }
00588 if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
00589 cur_cat = ast_category_new(q.category);
00590 if (!cur_cat) {
00591 ast_log(LOG_WARNING, "Out of memory!\n");
00592 break;
00593 }
00594 strcpy(last, q.category);
00595 last_cat_metric = q.cat_metric;
00596 ast_category_append(cfg, cur_cat);
00597 }
00598
00599 new_v = ast_variable_new(q.var_name, q.var_val);
00600 ast_variable_append(cur_cat, new_v);
00601 }
00602
00603 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00604 ast_odbc_release_obj(obj);
00605 return cfg;
00606 }
00607
00608 static struct ast_config_engine odbc_engine = {
00609 .name = "odbc",
00610 .load_func = config_odbc,
00611 .realtime_func = realtime_odbc,
00612 .realtime_multi_func = realtime_multi_odbc,
00613 .update_func = update_odbc
00614 };
00615
00616 static int unload_module (void)
00617 {
00618 ast_module_user_hangup_all();
00619 ast_config_engine_deregister(&odbc_engine);
00620 if (option_verbose)
00621 ast_verbose("res_config_odbc unloaded.\n");
00622 return 0;
00623 }
00624
00625 static int load_module (void)
00626 {
00627 ast_config_engine_register(&odbc_engine);
00628 if (option_verbose)
00629 ast_verbose("res_config_odbc loaded.\n");
00630 return 0;
00631 }
00632
00633 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC Configuration",
00634 .load = load_module,
00635 .unload = unload_module,
00636 );