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