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 #include "asterisk.h"
00033
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328209 $")
00035
00036 #include <sys/types.h>
00037 #include <time.h>
00038
00039 #include <sql.h>
00040 #include <sqlext.h>
00041 #include <sqltypes.h>
00042
00043 #include "asterisk/config.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/lock.h"
00046 #include "asterisk/linkedlists.h"
00047 #include "asterisk/res_odbc.h"
00048 #include "asterisk/cdr.h"
00049 #include "asterisk/module.h"
00050
00051 #define CONFIG "cdr_adaptive_odbc.conf"
00052
00053 static const char name[] = "Adaptive ODBC";
00054
00055 static int maxsize = 512, maxsize2 = 512;
00056
00057 struct columns {
00058 char *name;
00059 char *cdrname;
00060 char *filtervalue;
00061 char *staticvalue;
00062 SQLSMALLINT type;
00063 SQLINTEGER size;
00064 SQLSMALLINT decimals;
00065 SQLSMALLINT radix;
00066 SQLSMALLINT nullable;
00067 SQLINTEGER octetlen;
00068 AST_LIST_ENTRY(columns) list;
00069 };
00070
00071 struct tables {
00072 char *connection;
00073 char *table;
00074 unsigned int usegmtime:1;
00075 AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;
00076 AST_RWLIST_ENTRY(tables) list;
00077 };
00078
00079 static AST_RWLIST_HEAD_STATIC(odbc_tables, tables);
00080
00081 static int load_config(void)
00082 {
00083 struct ast_config *cfg;
00084 struct ast_variable *var;
00085 const char *tmp, *catg;
00086 struct tables *tableptr;
00087 struct columns *entry;
00088 struct odbc_obj *obj;
00089 char columnname[80];
00090 char connection[40];
00091 char table[40];
00092 int lenconnection, lentable, usegmtime = 0;
00093 SQLLEN sqlptr;
00094 int res = 0;
00095 SQLHSTMT stmt = NULL;
00096 struct ast_flags config_flags = { 0 };
00097
00098 cfg = ast_config_load(CONFIG, config_flags);
00099 if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00100 ast_log(LOG_WARNING, "Unable to load " CONFIG ". No adaptive ODBC CDRs.\n");
00101 return -1;
00102 }
00103
00104 for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
00105 var = ast_variable_browse(cfg, catg);
00106 if (!var)
00107 continue;
00108
00109 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
00110 ast_log(LOG_WARNING, "No connection parameter found in '%s'. Skipping.\n", catg);
00111 continue;
00112 }
00113 ast_copy_string(connection, tmp, sizeof(connection));
00114 lenconnection = strlen(connection);
00115
00116 if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) {
00117 usegmtime = ast_true(tmp);
00118 }
00119
00120
00121 obj = ast_odbc_request_obj(connection, 1);
00122 if (!obj) {
00123 ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
00124 continue;
00125 }
00126
00127 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
00128 ast_log(LOG_NOTICE, "No table name found. Assuming 'cdr'.\n");
00129 tmp = "cdr";
00130 }
00131 ast_copy_string(table, tmp, sizeof(table));
00132 lentable = strlen(table);
00133
00134 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00135 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00136 ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
00137 ast_odbc_release_obj(obj);
00138 continue;
00139 }
00140
00141 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
00142 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00143 ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'. Skipping.\n", connection);
00144 ast_odbc_release_obj(obj);
00145 continue;
00146 }
00147
00148 tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
00149 if (!tableptr) {
00150 ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
00151 ast_odbc_release_obj(obj);
00152 res = -1;
00153 break;
00154 }
00155
00156 tableptr->usegmtime = usegmtime;
00157 tableptr->connection = (char *)tableptr + sizeof(*tableptr);
00158 tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
00159 ast_copy_string(tableptr->connection, connection, lenconnection + 1);
00160 ast_copy_string(tableptr->table, table, lentable + 1);
00161
00162 ast_verb(3, "Found adaptive CDR table %s@%s.\n", tableptr->table, tableptr->connection);
00163
00164
00165 for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
00166 if (strncmp(var->name, "filter", 6) == 0) {
00167 char *cdrvar = ast_strdupa(var->name + 6);
00168 cdrvar = ast_strip(cdrvar);
00169 ast_verb(3, "Found filter %s for cdr variable %s in %s@%s\n", var->value, cdrvar, tableptr->table, tableptr->connection);
00170
00171 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->value) + 1);
00172 if (!entry) {
00173 ast_log(LOG_ERROR, "Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection);
00174 res = -1;
00175 break;
00176 }
00177
00178
00179 entry->name = NULL;
00180 entry->cdrname = (char *)entry + sizeof(*entry);
00181 entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(cdrvar) + 1;
00182 strcpy(entry->cdrname, cdrvar);
00183 strcpy(entry->filtervalue, var->value);
00184
00185 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00186 }
00187 }
00188
00189 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
00190 char *cdrvar = "", *staticvalue = "";
00191
00192 SQLGetData(stmt, 4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
00193
00194
00195
00196
00197
00198
00199
00200 for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
00201 if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
00202 char *alias = ast_strdupa(var->name + 5);
00203 cdrvar = ast_strip(alias);
00204 ast_verb(3, "Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->table, tableptr->connection);
00205 break;
00206 } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) {
00207 char *item = ast_strdupa(var->name + 6);
00208 item = ast_strip(item);
00209 if (item[0] == '"' && item[strlen(item) - 1] == '"') {
00210
00211 item[strlen(item) - 1] = '\0';
00212 item++;
00213 }
00214 staticvalue = item;
00215 }
00216 }
00217
00218 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1);
00219 if (!entry) {
00220 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
00221 res = -1;
00222 break;
00223 }
00224 entry->name = (char *)entry + sizeof(*entry);
00225 strcpy(entry->name, columnname);
00226
00227 if (!ast_strlen_zero(cdrvar)) {
00228 entry->cdrname = entry->name + strlen(columnname) + 1;
00229 strcpy(entry->cdrname, cdrvar);
00230 } else {
00231 entry->cdrname = (char *)entry + sizeof(*entry);
00232 }
00233
00234 if (!ast_strlen_zero(staticvalue)) {
00235 entry->staticvalue = entry->cdrname + strlen(entry->cdrname) + 1;
00236 strcpy(entry->staticvalue, staticvalue);
00237 }
00238
00239 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
00240 SQLGetData(stmt, 7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
00241 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
00242 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
00243 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
00244 SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
00245
00246
00247
00248
00249 if (entry->octetlen == 0)
00250 entry->octetlen = entry->size;
00251
00252 ast_verb(10, "Found %s column with type %hd with len %ld, octetlen %ld, and numlen (%hd,%hd)\n", entry->name, entry->type, (long) entry->size, (long) entry->octetlen, entry->decimals, entry->radix);
00253
00254 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00255 res = 0;
00256 }
00257
00258 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00259 ast_odbc_release_obj(obj);
00260
00261 if (AST_LIST_FIRST(&(tableptr->columns)))
00262 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
00263 else
00264 ast_free(tableptr);
00265 }
00266 return res;
00267 }
00268
00269 static int free_config(void)
00270 {
00271 struct tables *table;
00272 struct columns *entry;
00273 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
00274 while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
00275 ast_free(entry);
00276 }
00277 ast_free(table);
00278 }
00279 return 0;
00280 }
00281
00282 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
00283 {
00284 int res, i;
00285 SQLHSTMT stmt;
00286 SQLINTEGER nativeerror = 0, numfields = 0;
00287 SQLSMALLINT diagbytes = 0;
00288 unsigned char state[10], diagnostic[256];
00289
00290 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00291 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00292 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00293 return NULL;
00294 }
00295
00296 res = SQLPrepare(stmt, (unsigned char *) data, SQL_NTS);
00297 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00298 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *) data);
00299 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00300 for (i = 0; i < numfields; i++) {
00301 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00302 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00303 if (i > 10) {
00304 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00305 break;
00306 }
00307 }
00308 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00309 return NULL;
00310 }
00311
00312 return stmt;
00313 }
00314
00315 #define LENGTHEN_BUF1(size) \
00316 do { \
00317 \
00318 if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
00319 if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 1) / 512 + 1) * 512) != 0) { \
00320 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
00321 ast_free(sql); \
00322 ast_free(sql2); \
00323 AST_RWLIST_UNLOCK(&odbc_tables); \
00324 return -1; \
00325 } \
00326 } \
00327 } while (0)
00328
00329 #define LENGTHEN_BUF2(size) \
00330 do { \
00331 if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
00332 if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
00333 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
00334 ast_free(sql); \
00335 ast_free(sql2); \
00336 AST_RWLIST_UNLOCK(&odbc_tables); \
00337 return -1; \
00338 } \
00339 } \
00340 } while (0)
00341
00342 static int odbc_log(struct ast_cdr *cdr)
00343 {
00344 struct tables *tableptr;
00345 struct columns *entry;
00346 struct odbc_obj *obj;
00347 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00348 char *tmp;
00349 char colbuf[1024], *colptr;
00350 SQLHSTMT stmt = NULL;
00351 SQLLEN rows = 0;
00352
00353 if (!sql || !sql2) {
00354 if (sql)
00355 ast_free(sql);
00356 if (sql2)
00357 ast_free(sql2);
00358 return -1;
00359 }
00360
00361 if (AST_RWLIST_RDLOCK(&odbc_tables)) {
00362 ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n");
00363 ast_free(sql);
00364 ast_free(sql2);
00365 return -1;
00366 }
00367
00368 AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
00369 int first = 1;
00370 ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
00371 ast_str_set(&sql2, 0, " VALUES (");
00372
00373
00374 if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
00375 ast_log(LOG_WARNING, "cdr_adaptive_odbc: Unable to retrieve database handle for '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
00376 continue;
00377 }
00378
00379 AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
00380 int datefield = 0;
00381 if (strcasecmp(entry->cdrname, "start") == 0) {
00382 datefield = 1;
00383 } else if (strcasecmp(entry->cdrname, "answer") == 0) {
00384 datefield = 2;
00385 } else if (strcasecmp(entry->cdrname, "end") == 0) {
00386 datefield = 3;
00387 }
00388
00389
00390 if (entry->staticvalue) {
00391 colptr = ast_strdupa(entry->staticvalue);
00392 } else if (datefield && tableptr->usegmtime) {
00393 struct timeval date_tv = (datefield == 1) ? cdr->start : (datefield == 2) ? cdr->answer : cdr->end;
00394 struct ast_tm tm = { 0, };
00395 ast_localtime(&date_tv, &tm, "UTC");
00396 ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
00397 colptr = colbuf;
00398 } else {
00399 ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1);
00400 }
00401
00402 if (colptr) {
00403
00404
00405
00406
00407 if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
00408 ast_verb(4, "CDR column '%s' with value '%s' does not match filter of"
00409 " '%s'. Cancelling this CDR.\n",
00410 entry->cdrname, colptr, entry->filtervalue);
00411 goto early_release;
00412 }
00413
00414
00415 if (ast_strlen_zero(entry->name))
00416 continue;
00417
00418 LENGTHEN_BUF1(strlen(entry->name));
00419
00420 switch (entry->type) {
00421 case SQL_CHAR:
00422 case SQL_VARCHAR:
00423 case SQL_LONGVARCHAR:
00424 case SQL_BINARY:
00425 case SQL_VARBINARY:
00426 case SQL_LONGVARBINARY:
00427 case SQL_GUID:
00428
00429
00430
00431 if (strcasecmp(entry->name, "disposition") == 0) {
00432 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
00433 } else if (strcasecmp(entry->name, "amaflags") == 0) {
00434 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
00435 }
00436
00437
00438 if (entry->type != SQL_GUID) {
00439 if (strlen(colptr) > entry->octetlen) {
00440 colptr[entry->octetlen] = '\0';
00441 }
00442 }
00443
00444 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00445 LENGTHEN_BUF2(strlen(colptr));
00446
00447
00448 ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
00449 for (tmp = colptr; *tmp; tmp++) {
00450 if (*tmp == '\'') {
00451 ast_str_append(&sql2, 0, "''");
00452 } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
00453 ast_str_append(&sql2, 0, "\\\\");
00454 } else {
00455 ast_str_append(&sql2, 0, "%c", *tmp);
00456 }
00457 }
00458 ast_str_append(&sql2, 0, "'");
00459 break;
00460 case SQL_TYPE_DATE:
00461 if (ast_strlen_zero(colptr)) {
00462 continue;
00463 } else {
00464 int year = 0, month = 0, day = 0;
00465 if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
00466 month <= 0 || month > 12 || day < 0 || day > 31 ||
00467 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00468 (month == 2 && year % 400 == 0 && day > 29) ||
00469 (month == 2 && year % 100 == 0 && day > 28) ||
00470 (month == 2 && year % 4 == 0 && day > 29) ||
00471 (month == 2 && year % 4 != 0 && day > 28)) {
00472 ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
00473 continue;
00474 }
00475
00476 if (year > 0 && year < 100) {
00477 year += 2000;
00478 }
00479
00480 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00481 LENGTHEN_BUF2(17);
00482 ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
00483 }
00484 break;
00485 case SQL_TYPE_TIME:
00486 if (ast_strlen_zero(colptr)) {
00487 continue;
00488 } else {
00489 int hour = 0, minute = 0, second = 0;
00490 int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
00491
00492 if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
00493 ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
00494 continue;
00495 }
00496
00497 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00498 LENGTHEN_BUF2(15);
00499 ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
00500 }
00501 break;
00502 case SQL_TYPE_TIMESTAMP:
00503 case SQL_TIMESTAMP:
00504 if (ast_strlen_zero(colptr)) {
00505 continue;
00506 } else {
00507 int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
00508 int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
00509
00510 if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
00511 month <= 0 || month > 12 || day < 0 || day > 31 ||
00512 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00513 (month == 2 && year % 400 == 0 && day > 29) ||
00514 (month == 2 && year % 100 == 0 && day > 28) ||
00515 (month == 2 && year % 4 == 0 && day > 29) ||
00516 (month == 2 && year % 4 != 0 && day > 28) ||
00517 hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
00518 ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
00519 continue;
00520 }
00521
00522 if (year > 0 && year < 100) {
00523 year += 2000;
00524 }
00525
00526 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00527 LENGTHEN_BUF2(26);
00528 ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
00529 }
00530 break;
00531 case SQL_INTEGER:
00532 if (ast_strlen_zero(colptr)) {
00533 continue;
00534 } else {
00535 int integer = 0;
00536 if (sscanf(colptr, "%30d", &integer) != 1) {
00537 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00538 continue;
00539 }
00540
00541 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00542 LENGTHEN_BUF2(12);
00543 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00544 }
00545 break;
00546 case SQL_BIGINT:
00547 if (ast_strlen_zero(colptr)) {
00548 continue;
00549 } else {
00550 long long integer = 0;
00551 if (sscanf(colptr, "%30lld", &integer) != 1) {
00552 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00553 continue;
00554 }
00555
00556 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00557 LENGTHEN_BUF2(24);
00558 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
00559 }
00560 break;
00561 case SQL_SMALLINT:
00562 if (ast_strlen_zero(colptr)) {
00563 continue;
00564 } else {
00565 short integer = 0;
00566 if (sscanf(colptr, "%30hd", &integer) != 1) {
00567 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00568 continue;
00569 }
00570
00571 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00572 LENGTHEN_BUF2(6);
00573 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00574 }
00575 break;
00576 case SQL_TINYINT:
00577 if (ast_strlen_zero(colptr)) {
00578 continue;
00579 } else {
00580 char integer = 0;
00581 if (sscanf(colptr, "%30hhd", &integer) != 1) {
00582 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00583 continue;
00584 }
00585
00586 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00587 LENGTHEN_BUF2(4);
00588 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00589 }
00590 break;
00591 case SQL_BIT:
00592 if (ast_strlen_zero(colptr)) {
00593 continue;
00594 } else {
00595 char integer = 0;
00596 if (sscanf(colptr, "%30hhd", &integer) != 1) {
00597 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00598 continue;
00599 }
00600 if (integer != 0)
00601 integer = 1;
00602
00603 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00604 LENGTHEN_BUF2(2);
00605 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00606 }
00607 break;
00608 case SQL_NUMERIC:
00609 case SQL_DECIMAL:
00610 if (ast_strlen_zero(colptr)) {
00611 continue;
00612 } else {
00613 double number = 0.0;
00614
00615 if (!strcasecmp(entry->cdrname, "billsec")) {
00616 if (!ast_tvzero(cdr->answer)) {
00617 snprintf(colbuf, sizeof(colbuf), "%lf",
00618 (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
00619 } else {
00620 ast_copy_string(colbuf, "0", sizeof(colbuf));
00621 }
00622 } else if (!strcasecmp(entry->cdrname, "duration")) {
00623 snprintf(colbuf, sizeof(colbuf), "%lf",
00624 (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
00625
00626 if (!ast_strlen_zero(colbuf)) {
00627 colptr = colbuf;
00628 }
00629 }
00630
00631 if (sscanf(colptr, "%30lf", &number) != 1) {
00632 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00633 continue;
00634 }
00635
00636 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00637 LENGTHEN_BUF2(entry->decimals);
00638 ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
00639 }
00640 break;
00641 case SQL_FLOAT:
00642 case SQL_REAL:
00643 case SQL_DOUBLE:
00644 if (ast_strlen_zero(colptr)) {
00645 continue;
00646 } else {
00647 double number = 0.0;
00648
00649 if (!strcasecmp(entry->cdrname, "billsec")) {
00650 if (!ast_tvzero(cdr->answer)) {
00651 snprintf(colbuf, sizeof(colbuf), "%lf",
00652 (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
00653 } else {
00654 ast_copy_string(colbuf, "0", sizeof(colbuf));
00655 }
00656 } else if (!strcasecmp(entry->cdrname, "duration")) {
00657 snprintf(colbuf, sizeof(colbuf), "%lf",
00658 (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
00659
00660 if (!ast_strlen_zero(colbuf)) {
00661 colptr = colbuf;
00662 }
00663 }
00664
00665 if (sscanf(colptr, "%30lf", &number) != 1) {
00666 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00667 continue;
00668 }
00669
00670 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00671 LENGTHEN_BUF2(entry->decimals);
00672 ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
00673 }
00674 break;
00675 default:
00676 ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
00677 continue;
00678 }
00679 first = 0;
00680 }
00681 }
00682
00683
00684 LENGTHEN_BUF1(ast_str_strlen(sql2));
00685 ast_str_append(&sql, 0, ")");
00686 ast_str_append(&sql2, 0, ")");
00687 ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
00688
00689 ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00690
00691 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
00692 if (stmt) {
00693 SQLRowCount(stmt, &rows);
00694 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00695 }
00696 if (rows == 0) {
00697 ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
00698 }
00699 early_release:
00700 ast_odbc_release_obj(obj);
00701 }
00702 AST_RWLIST_UNLOCK(&odbc_tables);
00703
00704
00705 if (ast_str_strlen(sql) > maxsize) {
00706 maxsize = ast_str_strlen(sql);
00707 }
00708 if (ast_str_strlen(sql2) > maxsize2) {
00709 maxsize2 = ast_str_strlen(sql2);
00710 }
00711
00712 ast_free(sql);
00713 ast_free(sql2);
00714 return 0;
00715 }
00716
00717 static int unload_module(void)
00718 {
00719 ast_cdr_unregister(name);
00720 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00721 ast_cdr_register(name, ast_module_info->description, odbc_log);
00722 ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
00723 return -1;
00724 }
00725
00726 free_config();
00727 AST_RWLIST_UNLOCK(&odbc_tables);
00728 return 0;
00729 }
00730
00731 static int load_module(void)
00732 {
00733 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00734 ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n");
00735 return 0;
00736 }
00737
00738 load_config();
00739 AST_RWLIST_UNLOCK(&odbc_tables);
00740 ast_cdr_register(name, ast_module_info->description, odbc_log);
00741 return 0;
00742 }
00743
00744 static int reload(void)
00745 {
00746 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00747 ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
00748 return -1;
00749 }
00750
00751 free_config();
00752 load_config();
00753 AST_RWLIST_UNLOCK(&odbc_tables);
00754 return 0;
00755 }
00756
00757 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Adaptive ODBC CDR backend",
00758 .load = load_module,
00759 .unload = unload_module,
00760 .reload = reload,
00761 .load_pri = AST_MODPRI_CDR_DRIVER,
00762 );
00763