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