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: 413586 $")
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 ast_config_destroy(cfg);
00267 return res;
00268 }
00269
00270 static int free_config(void)
00271 {
00272 struct tables *table;
00273 struct columns *entry;
00274 while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
00275 while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
00276 ast_free(entry);
00277 }
00278 ast_free(table);
00279 }
00280 return 0;
00281 }
00282
00283 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
00284 {
00285 int res, i;
00286 SQLHSTMT stmt;
00287 SQLINTEGER nativeerror = 0, numfields = 0;
00288 SQLSMALLINT diagbytes = 0;
00289 unsigned char state[10], diagnostic[256];
00290
00291 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00292 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00293 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00294 return NULL;
00295 }
00296
00297 res = SQLPrepare(stmt, (unsigned char *) data, SQL_NTS);
00298 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00299 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", (char *) data);
00300 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00301 for (i = 0; i < numfields; i++) {
00302 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00303 ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00304 if (i > 10) {
00305 ast_log(LOG_WARNING, "Oh, that was good. There are really %d diagnostics?\n", (int)numfields);
00306 break;
00307 }
00308 }
00309 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00310 return NULL;
00311 }
00312
00313 return stmt;
00314 }
00315
00316 #define LENGTHEN_BUF1(size) \
00317 do { \
00318 \
00319 if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
00320 if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 1) / 512 + 1) * 512) != 0) { \
00321 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
00322 ast_free(sql); \
00323 ast_free(sql2); \
00324 AST_RWLIST_UNLOCK(&odbc_tables); \
00325 return -1; \
00326 } \
00327 } \
00328 } while (0)
00329
00330 #define LENGTHEN_BUF2(size) \
00331 do { \
00332 if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
00333 if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
00334 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
00335 ast_free(sql); \
00336 ast_free(sql2); \
00337 AST_RWLIST_UNLOCK(&odbc_tables); \
00338 return -1; \
00339 } \
00340 } \
00341 } while (0)
00342
00343 static int odbc_log(struct ast_cdr *cdr)
00344 {
00345 struct tables *tableptr;
00346 struct columns *entry;
00347 struct odbc_obj *obj;
00348 struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00349 char *tmp;
00350 char colbuf[1024], *colptr;
00351 SQLHSTMT stmt = NULL;
00352 SQLLEN rows = 0;
00353
00354 if (!sql || !sql2) {
00355 if (sql)
00356 ast_free(sql);
00357 if (sql2)
00358 ast_free(sql2);
00359 return -1;
00360 }
00361
00362 if (AST_RWLIST_RDLOCK(&odbc_tables)) {
00363 ast_log(LOG_ERROR, "Unable to lock table list. Insert CDR(s) failed.\n");
00364 ast_free(sql);
00365 ast_free(sql2);
00366 return -1;
00367 }
00368
00369 AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
00370 int first = 1;
00371 ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
00372 ast_str_set(&sql2, 0, " VALUES (");
00373
00374
00375 if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
00376 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));
00377 continue;
00378 }
00379
00380 AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
00381 int datefield = 0;
00382 if (strcasecmp(entry->cdrname, "start") == 0) {
00383 datefield = 1;
00384 } else if (strcasecmp(entry->cdrname, "answer") == 0) {
00385 datefield = 2;
00386 } else if (strcasecmp(entry->cdrname, "end") == 0) {
00387 datefield = 3;
00388 }
00389
00390
00391 if (entry->staticvalue) {
00392 colptr = ast_strdupa(entry->staticvalue);
00393 } else if (datefield && tableptr->usegmtime) {
00394 struct timeval date_tv = (datefield == 1) ? cdr->start : (datefield == 2) ? cdr->answer : cdr->end;
00395 struct ast_tm tm = { 0, };
00396 ast_localtime(&date_tv, &tm, "UTC");
00397 ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S", &tm);
00398 colptr = colbuf;
00399 } else {
00400 ast_cdr_getvar(cdr, entry->cdrname, &colptr, colbuf, sizeof(colbuf), 0, datefield ? 0 : 1);
00401 }
00402
00403 if (colptr) {
00404
00405
00406
00407
00408 if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
00409 ast_verb(4, "CDR column '%s' with value '%s' does not match filter of"
00410 " '%s'. Cancelling this CDR.\n",
00411 entry->cdrname, colptr, entry->filtervalue);
00412 goto early_release;
00413 }
00414
00415
00416 if (ast_strlen_zero(entry->name))
00417 continue;
00418
00419 LENGTHEN_BUF1(strlen(entry->name));
00420
00421 switch (entry->type) {
00422 case SQL_CHAR:
00423 case SQL_VARCHAR:
00424 case SQL_LONGVARCHAR:
00425 #ifdef HAVE_ODBC_WCHAR
00426 case SQL_WCHAR:
00427 case SQL_WVARCHAR:
00428 case SQL_WLONGVARCHAR:
00429 #endif
00430 case SQL_BINARY:
00431 case SQL_VARBINARY:
00432 case SQL_LONGVARBINARY:
00433 case SQL_GUID:
00434
00435
00436
00437 if (strcasecmp(entry->name, "disposition") == 0) {
00438 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
00439 } else if (strcasecmp(entry->name, "amaflags") == 0) {
00440 ast_cdr_getvar(cdr, entry->name, &colptr, colbuf, sizeof(colbuf), 0, 0);
00441 }
00442
00443
00444 if (entry->type != SQL_GUID) {
00445 if (strlen(colptr) > entry->octetlen) {
00446 colptr[entry->octetlen] = '\0';
00447 }
00448 }
00449
00450 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00451 LENGTHEN_BUF2(strlen(colptr));
00452
00453
00454 ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
00455 for (tmp = colptr; *tmp; tmp++) {
00456 if (*tmp == '\'') {
00457 ast_str_append(&sql2, 0, "''");
00458 } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
00459 ast_str_append(&sql2, 0, "\\\\");
00460 } else {
00461 ast_str_append(&sql2, 0, "%c", *tmp);
00462 }
00463 }
00464 ast_str_append(&sql2, 0, "'");
00465 break;
00466 case SQL_TYPE_DATE:
00467 if (ast_strlen_zero(colptr)) {
00468 continue;
00469 } else {
00470 int year = 0, month = 0, day = 0;
00471 if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
00472 month <= 0 || month > 12 || day < 0 || day > 31 ||
00473 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00474 (month == 2 && year % 400 == 0 && day > 29) ||
00475 (month == 2 && year % 100 == 0 && day > 28) ||
00476 (month == 2 && year % 4 == 0 && day > 29) ||
00477 (month == 2 && year % 4 != 0 && day > 28)) {
00478 ast_log(LOG_WARNING, "CDR variable %s is not a valid date ('%s').\n", entry->name, colptr);
00479 continue;
00480 }
00481
00482 if (year > 0 && year < 100) {
00483 year += 2000;
00484 }
00485
00486 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00487 LENGTHEN_BUF2(17);
00488 ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
00489 }
00490 break;
00491 case SQL_TYPE_TIME:
00492 if (ast_strlen_zero(colptr)) {
00493 continue;
00494 } else {
00495 int hour = 0, minute = 0, second = 0;
00496 int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
00497
00498 if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
00499 ast_log(LOG_WARNING, "CDR variable %s is not a valid time ('%s').\n", entry->name, colptr);
00500 continue;
00501 }
00502
00503 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00504 LENGTHEN_BUF2(15);
00505 ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
00506 }
00507 break;
00508 case SQL_TYPE_TIMESTAMP:
00509 case SQL_TIMESTAMP:
00510 if (ast_strlen_zero(colptr)) {
00511 continue;
00512 } else {
00513 int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
00514 int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
00515
00516 if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
00517 month <= 0 || month > 12 || day < 0 || day > 31 ||
00518 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00519 (month == 2 && year % 400 == 0 && day > 29) ||
00520 (month == 2 && year % 100 == 0 && day > 28) ||
00521 (month == 2 && year % 4 == 0 && day > 29) ||
00522 (month == 2 && year % 4 != 0 && day > 28) ||
00523 hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
00524 ast_log(LOG_WARNING, "CDR variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
00525 continue;
00526 }
00527
00528 if (year > 0 && year < 100) {
00529 year += 2000;
00530 }
00531
00532 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00533 LENGTHEN_BUF2(26);
00534 ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
00535 }
00536 break;
00537 case SQL_INTEGER:
00538 if (ast_strlen_zero(colptr)) {
00539 continue;
00540 } else {
00541 int integer = 0;
00542 if (sscanf(colptr, "%30d", &integer) != 1) {
00543 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00544 continue;
00545 }
00546
00547 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00548 LENGTHEN_BUF2(12);
00549 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00550 }
00551 break;
00552 case SQL_BIGINT:
00553 if (ast_strlen_zero(colptr)) {
00554 continue;
00555 } else {
00556 long long integer = 0;
00557 if (sscanf(colptr, "%30lld", &integer) != 1) {
00558 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00559 continue;
00560 }
00561
00562 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00563 LENGTHEN_BUF2(24);
00564 ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
00565 }
00566 break;
00567 case SQL_SMALLINT:
00568 if (ast_strlen_zero(colptr)) {
00569 continue;
00570 } else {
00571 short integer = 0;
00572 if (sscanf(colptr, "%30hd", &integer) != 1) {
00573 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00574 continue;
00575 }
00576
00577 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00578 LENGTHEN_BUF2(6);
00579 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00580 }
00581 break;
00582 case SQL_TINYINT:
00583 if (ast_strlen_zero(colptr)) {
00584 continue;
00585 } else {
00586 signed char integer = 0;
00587 if (sscanf(colptr, "%30hhd", &integer) != 1) {
00588 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00589 continue;
00590 }
00591
00592 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00593 LENGTHEN_BUF2(4);
00594 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00595 }
00596 break;
00597 case SQL_BIT:
00598 if (ast_strlen_zero(colptr)) {
00599 continue;
00600 } else {
00601 signed char integer = 0;
00602 if (sscanf(colptr, "%30hhd", &integer) != 1) {
00603 ast_log(LOG_WARNING, "CDR variable %s is not an integer.\n", entry->name);
00604 continue;
00605 }
00606 if (integer != 0)
00607 integer = 1;
00608
00609 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00610 LENGTHEN_BUF2(2);
00611 ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00612 }
00613 break;
00614 case SQL_NUMERIC:
00615 case SQL_DECIMAL:
00616 if (ast_strlen_zero(colptr)) {
00617 continue;
00618 } else {
00619 double number = 0.0;
00620
00621 if (!strcasecmp(entry->cdrname, "billsec")) {
00622 if (!ast_tvzero(cdr->answer)) {
00623 snprintf(colbuf, sizeof(colbuf), "%lf",
00624 (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
00625 } else {
00626 ast_copy_string(colbuf, "0", sizeof(colbuf));
00627 }
00628 } else if (!strcasecmp(entry->cdrname, "duration")) {
00629 snprintf(colbuf, sizeof(colbuf), "%lf",
00630 (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
00631
00632 if (!ast_strlen_zero(colbuf)) {
00633 colptr = colbuf;
00634 }
00635 }
00636
00637 if (sscanf(colptr, "%30lf", &number) != 1) {
00638 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00639 continue;
00640 }
00641
00642 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00643 LENGTHEN_BUF2(entry->decimals);
00644 ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
00645 }
00646 break;
00647 case SQL_FLOAT:
00648 case SQL_REAL:
00649 case SQL_DOUBLE:
00650 if (ast_strlen_zero(colptr)) {
00651 continue;
00652 } else {
00653 double number = 0.0;
00654
00655 if (!strcasecmp(entry->cdrname, "billsec")) {
00656 if (!ast_tvzero(cdr->answer)) {
00657 snprintf(colbuf, sizeof(colbuf), "%lf",
00658 (double) (ast_tvdiff_us(cdr->end, cdr->answer) / 1000000.0));
00659 } else {
00660 ast_copy_string(colbuf, "0", sizeof(colbuf));
00661 }
00662 } else if (!strcasecmp(entry->cdrname, "duration")) {
00663 snprintf(colbuf, sizeof(colbuf), "%lf",
00664 (double) (ast_tvdiff_us(cdr->end, cdr->start) / 1000000.0));
00665
00666 if (!ast_strlen_zero(colbuf)) {
00667 colptr = colbuf;
00668 }
00669 }
00670
00671 if (sscanf(colptr, "%30lf", &number) != 1) {
00672 ast_log(LOG_WARNING, "CDR variable %s is not an numeric type.\n", entry->name);
00673 continue;
00674 }
00675
00676 ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00677 LENGTHEN_BUF2(entry->decimals);
00678 ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
00679 }
00680 break;
00681 default:
00682 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);
00683 continue;
00684 }
00685 first = 0;
00686 } else if (entry->filtervalue && entry->filtervalue[0] != '\0') {
00687 ast_verb(4, "CDR column '%s' was not set and does not match filter of"
00688 " '%s'. Cancelling this CDR.\n",
00689 entry->cdrname, entry->filtervalue);
00690 goto early_release;
00691 }
00692 }
00693
00694
00695 LENGTHEN_BUF1(ast_str_strlen(sql2));
00696 ast_str_append(&sql, 0, ")");
00697 ast_str_append(&sql2, 0, ")");
00698 ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
00699
00700 ast_verb(11, "[%s]\n", ast_str_buffer(sql));
00701
00702 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
00703 if (stmt) {
00704 SQLRowCount(stmt, &rows);
00705 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00706 }
00707 if (rows == 0) {
00708 ast_log(LOG_WARNING, "cdr_adaptive_odbc: Insert failed on '%s:%s'. CDR failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
00709 }
00710 early_release:
00711 ast_odbc_release_obj(obj);
00712 }
00713 AST_RWLIST_UNLOCK(&odbc_tables);
00714
00715
00716 if (ast_str_strlen(sql) > maxsize) {
00717 maxsize = ast_str_strlen(sql);
00718 }
00719 if (ast_str_strlen(sql2) > maxsize2) {
00720 maxsize2 = ast_str_strlen(sql2);
00721 }
00722
00723 ast_free(sql);
00724 ast_free(sql2);
00725 return 0;
00726 }
00727
00728 static int unload_module(void)
00729 {
00730 ast_cdr_unregister(name);
00731 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00732 ast_cdr_register(name, ast_module_info->description, odbc_log);
00733 ast_log(LOG_ERROR, "Unable to lock column list. Unload failed.\n");
00734 return -1;
00735 }
00736
00737 free_config();
00738 AST_RWLIST_UNLOCK(&odbc_tables);
00739 return 0;
00740 }
00741
00742 static int load_module(void)
00743 {
00744 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00745 ast_log(LOG_ERROR, "Unable to lock column list. Load failed.\n");
00746 return 0;
00747 }
00748
00749 load_config();
00750 AST_RWLIST_UNLOCK(&odbc_tables);
00751 ast_cdr_register(name, ast_module_info->description, odbc_log);
00752 return 0;
00753 }
00754
00755 static int reload(void)
00756 {
00757 if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00758 ast_log(LOG_ERROR, "Unable to lock column list. Reload failed.\n");
00759 return -1;
00760 }
00761
00762 free_config();
00763 load_config();
00764 AST_RWLIST_UNLOCK(&odbc_tables);
00765 return 0;
00766 }
00767
00768 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Adaptive ODBC CDR backend",
00769 .load = load_module,
00770 .unload = unload_module,
00771 .reload = reload,
00772 .load_pri = AST_MODPRI_CDR_DRIVER,
00773 );
00774