36 #include <sys/types.h>
51 #define CONFIG "cdr_adaptive_odbc.conf"
53 static const char name[] =
"Adaptive ODBC";
85 const char *tmp, *catg;
86 struct tables *tableptr;
87 struct columns *entry;
92 int lenconnection, lentable,
usegmtime = 0;
114 lenconnection = strlen(connection);
123 ast_log(
LOG_WARNING,
"No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
132 lentable = strlen(table);
134 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->
con, &stmt);
135 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
141 res = SQLColumns(stmt, NULL, 0, NULL, 0, (
unsigned char *)table, SQL_NTS, (
unsigned char *)
"%", SQL_NTS);
142 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
143 ast_log(
LOG_ERROR,
"Unable to query database columns on connection '%s'. Skipping.\n", connection);
148 tableptr =
ast_calloc(
sizeof(
char),
sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
150 ast_log(
LOG_ERROR,
"Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
157 tableptr->
connection = (
char *)tableptr +
sizeof(*tableptr);
158 tableptr->
table = (
char *)tableptr +
sizeof(*tableptr) + lenconnection + 1;
166 if (strncmp(var->
name,
"filter", 6) == 0) {
171 entry =
ast_calloc(
sizeof(
char),
sizeof(*entry) + strlen(cdrvar) + 1 + strlen(var->
value) + 1);
173 ast_log(
LOG_ERROR,
"Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection);
180 entry->
cdrname = (
char *)entry +
sizeof(*entry);
181 entry->
filtervalue = (
char *)entry +
sizeof(*entry) + strlen(cdrvar) + 1;
182 strcpy(entry->
cdrname, cdrvar);
189 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
192 SQLGetData(stmt, 4, SQL_C_CHAR, columnname,
sizeof(columnname), &sqlptr);
201 if (strncmp(var->
name,
"alias", 5) == 0 && strcasecmp(var->
value, columnname) == 0) {
204 ast_verb(3,
"Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->
table, tableptr->
connection);
206 }
else if (strncmp(var->
name,
"static", 6) == 0 && strcasecmp(var->
value, columnname) == 0) {
209 if (item[0] ==
'"' && item[strlen(item) - 1] ==
'"') {
211 item[strlen(item) - 1] =
'\0';
218 entry =
ast_calloc(
sizeof(
char),
sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1 + strlen(
staticvalue) + 1);
220 ast_log(
LOG_ERROR,
"Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
224 entry->
name = (
char *)entry +
sizeof(*entry);
225 strcpy(entry->
name, columnname);
228 entry->
cdrname = entry->
name + strlen(columnname) + 1;
229 strcpy(entry->
cdrname, cdrvar);
231 entry->
cdrname = (
char *)entry +
sizeof(*entry);
239 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->
type,
sizeof(entry->
type), NULL);
240 SQLGetData(stmt, 7, SQL_C_LONG, &entry->
size,
sizeof(entry->
size), NULL);
241 SQLGetData(stmt, 9, SQL_C_SHORT, &entry->
decimals,
sizeof(entry->
decimals), NULL);
242 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->
radix,
sizeof(entry->
radix), NULL);
243 SQLGetData(stmt, 11, SQL_C_SHORT, &entry->
nullable,
sizeof(entry->
nullable), NULL);
244 SQLGetData(stmt, 16, SQL_C_LONG, &entry->
octetlen,
sizeof(entry->
octetlen), NULL);
252 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);
258 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
272 struct tables *
table;
287 SQLINTEGER nativeerror = 0, numfields = 0;
288 SQLSMALLINT diagbytes = 0;
289 unsigned char state[10], diagnostic[256];
291 res = SQLAllocHandle (SQL_HANDLE_STMT, obj->
con, &stmt);
292 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
297 res = SQLPrepare(stmt, (
unsigned char *) data, SQL_NTS);
298 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
300 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
301 for (i = 0; i < numfields; i++) {
302 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic,
sizeof(diagnostic), &diagbytes);
303 ast_log(
LOG_WARNING,
"SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
305 ast_log(
LOG_WARNING,
"Oh, that was good. There are really %d diagnostics?\n", (
int)numfields);
309 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
316 #define LENGTHEN_BUF1(size) \
319 if (ast_str_strlen(sql) + size + 1 > ast_str_size(sql)) { \
320 if (ast_str_make_space(&sql, ((ast_str_size(sql) + size + 1) / 512 + 1) * 512) != 0) { \
321 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
324 AST_RWLIST_UNLOCK(&odbc_tables); \
330 #define LENGTHEN_BUF2(size) \
332 if (ast_str_strlen(sql2) + size + 1 > ast_str_size(sql2)) { \
333 if (ast_str_make_space(&sql2, ((ast_str_size(sql2) + size + 3) / 512 + 1) * 512) != 0) { \
334 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
337 AST_RWLIST_UNLOCK(&odbc_tables); \
345 struct tables *tableptr;
350 char colbuf[1024], *colptr;
351 SQLHSTMT stmt = NULL;
382 if (strcasecmp(entry->
cdrname,
"start") == 0) {
384 }
else if (strcasecmp(entry->
cdrname,
"answer") == 0) {
386 }
else if (strcasecmp(entry->
cdrname,
"end") == 0) {
393 }
else if (datefield && tableptr->
usegmtime) {
394 struct timeval date_tv = (datefield == 1) ? cdr->
start : (datefield == 2) ? cdr->
answer : cdr->
end;
395 struct ast_tm tm = { 0, };
397 ast_strftime(colbuf,
sizeof(colbuf),
"%Y-%m-%d %H:%M:%S", &tm);
409 ast_verb(4,
"CDR column '%s' with value '%s' does not match filter of"
410 " '%s'. Cancelling this CDR.\n",
421 switch (entry->
type) {
424 case SQL_LONGVARCHAR:
425 #ifdef HAVE_ODBC_WCHAR
428 case SQL_WLONGVARCHAR:
432 case SQL_LONGVARBINARY:
437 if (strcasecmp(entry->
name,
"disposition") == 0) {
439 }
else if (strcasecmp(entry->
name,
"amaflags") == 0) {
444 if (entry->
type != SQL_GUID) {
445 if (strlen(colptr) > entry->
octetlen) {
455 for (tmp = colptr; *tmp; tmp++) {
470 int year = 0, month = 0, day = 0;
471 if (sscanf(colptr,
"%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
472 month <= 0 || month > 12 || day < 0 || day > 31 ||
473 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
474 (month == 2 && year % 400 == 0 && day > 29) ||
475 (month == 2 && year % 100 == 0 && day > 28) ||
476 (month == 2 && year % 4 == 0 && day > 29) ||
477 (month == 2 && year % 4 != 0 && day > 28)) {
482 if (year > 0 && year < 100) {
488 ast_str_append(&sql2, 0,
"%s{ d '%04d-%02d-%02d' }", first ?
"" :
",", year, month, day);
495 int hour = 0, minute = 0, second = 0;
496 int count = sscanf(colptr,
"%2d:%2d:%2d", &hour, &minute, &second);
498 if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
505 ast_str_append(&sql2, 0,
"%s{ t '%02d:%02d:%02d' }", first ?
"" :
",", hour, minute, second);
508 case SQL_TYPE_TIMESTAMP:
513 int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
514 int count = sscanf(colptr,
"%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
516 if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
517 month <= 0 || month > 12 || day < 0 || day > 31 ||
518 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
519 (month == 2 && year % 400 == 0 && day > 29) ||
520 (month == 2 && year % 100 == 0 && day > 28) ||
521 (month == 2 && year % 4 == 0 && day > 29) ||
522 (month == 2 && year % 4 != 0 && day > 28) ||
523 hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
528 if (year > 0 && year < 100) {
534 ast_str_append(&sql2, 0,
"%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ?
"" :
",", year, month, day, hour, minute, second);
542 if (sscanf(colptr,
"%30d", &integer) != 1) {
556 long long integer = 0;
557 if (sscanf(colptr,
"%30lld", &integer) != 1) {
572 if (sscanf(colptr,
"%30hd", &integer) != 1) {
586 signed char integer = 0;
587 if (sscanf(colptr,
"%30hhd", &integer) != 1) {
601 signed char integer = 0;
602 if (sscanf(colptr,
"%30hhd", &integer) != 1) {
621 if (!strcasecmp(entry->
cdrname,
"billsec")) {
623 snprintf(colbuf,
sizeof(colbuf),
"%lf",
628 }
else if (!strcasecmp(entry->
cdrname,
"duration")) {
629 snprintf(colbuf,
sizeof(colbuf),
"%lf",
637 if (sscanf(colptr,
"%30lf", &number) != 1) {
655 if (!strcasecmp(entry->
cdrname,
"billsec")) {
657 snprintf(colbuf,
sizeof(colbuf),
"%lf",
662 }
else if (!strcasecmp(entry->
cdrname,
"duration")) {
663 snprintf(colbuf,
sizeof(colbuf),
"%lf",
671 if (sscanf(colptr,
"%30lf", &number) != 1) {
687 ast_verb(4,
"CDR column '%s' was not set and does not match filter of"
688 " '%s'. Cancelling this CDR.\n",
704 SQLRowCount(stmt, &rows);
705 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
Checks if the database natively supports backslash as an escape character.
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
static int load_module(void)
Time-related functions and macros.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Structure for variables, used for configurations and for channel variables.
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.
Configuration File Parser.
#define LENGTHEN_BUF2(size)
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
struct ast_str * ast_str_create(size_t init_len)
Create a malloc'ed dynamic length string.
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
#define ast_verb(level,...)
void ast_config_destroy(struct ast_config *config)
Destroys a config.
void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
General Asterisk PBX channel definitions.
static int unload_module(void)
#define ast_config_load(filename, flags)
Load a config file.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
char * ast_category_browse(struct ast_config *config, const char *prev)
Goes through categories.
A set of macros to manage forward-linked lists.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
struct tables::odbc_columns columns
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
#define ast_strdupa(s)
duplicate a string in memory from the stack
Responsible for call detail data.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
static int free_config(void)
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
struct sla_ringing_trunk * first
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define ast_odbc_request_obj(a, b)
#define AST_RWLIST_REMOVE_HEAD
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
Structure used to handle boolean flags.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
#define AST_RWLIST_INSERT_TAIL
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
Prepares, executes, and returns the resulting statement handle.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
static int odbc_log(struct ast_cdr *cdr)
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
#define LENGTHEN_BUF1(size)
struct ast_variable * next
#define CONFIG_STATUS_FILEINVALID
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
void ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
static int load_config(void)
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.