59#define CONFIG  "cdr_adaptive_odbc.conf" 
   61static const char name[] = 
"Adaptive ODBC";
 
   96    const char *tmp, *catg;
 
  104    char quoted_identifiers;
 
  105    int lenconnection, lentable, lenschema, 
usegmtime = 0;
 
  108    SQLHSTMT stmt = 
NULL;
 
  127        lenconnection = strlen(connection);
 
  136            ast_log(
LOG_WARNING, 
"No such connection '%s' in the '%s' section of " CONFIG ".  Check res_odbc.conf.\n", connection, catg);
 
  145        lentable = strlen(table);
 
  151        lenschema = strlen(
schema);
 
  156        quoted_identifiers = tmp[0];
 
  157        if (strlen(tmp) > 1) {
 
  158            ast_log(
LOG_ERROR, 
"The quoted_identifiers setting only accepts a single character," 
  159                " while a value of '%s' was provided. This option has been disabled as a result.\n", tmp);
 
  160            quoted_identifiers = 
'\0';
 
  163        res = SQLAllocHandle(SQL_HANDLE_STMT, obj->
con, &stmt);
 
  164        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
 
  170        res = SQLColumns(stmt, 
NULL, 0, lenschema == 0 ? 
NULL : (
unsigned char *)
schema, SQL_NTS, (
unsigned char *)table, SQL_NTS, (
unsigned char *)
"%", SQL_NTS);
 
  171        if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
 
  172            ast_log(
LOG_ERROR, 
"Unable to query database columns on connection '%s'.  Skipping.\n", connection);
 
  173            SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
  178        tableptr = 
ast_calloc(
sizeof(
char), 
sizeof(*tableptr) + lenconnection + 1 + lentable + 1 + lenschema + 1 + 1);
 
  180            ast_log(
LOG_ERROR, 
"Out of memory creating entry for table '%s' on connection '%s'%s%s%s\n", table, connection,
 
  181                lenschema ? 
" (schema '" : 
"", lenschema ? 
schema : 
"", lenschema ? 
"')" : 
"");
 
  182            SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
  189        tableptr->
connection = (
char *)tableptr + 
sizeof(*tableptr);
 
  190        tableptr->
table = (
char *)tableptr + 
sizeof(*tableptr) + lenconnection + 1;
 
  191        tableptr->
schema = (
char *)tableptr + 
sizeof(*tableptr) + lenconnection + 1 + lentable + 1;
 
  201            if (strncmp(
var->name, 
"filter", 6) == 0) {
 
  205                if (cdrvar[strlen(cdrvar) - 1] == 
'!') {
 
  207                    cdrvar[strlen(cdrvar) - 1] = 
'\0';
 
  211                ast_verb(3, 
"Found filter %s'%s' for CDR variable %s in %s@%s\n", negate ? 
"!" : 
"", 
var->value, cdrvar, tableptr->
table, tableptr->
connection);
 
  213                entry = 
ast_calloc(
sizeof(
char), 
sizeof(*entry) + strlen(cdrvar) + 1 + strlen(
var->value) + 1);
 
  215                    ast_log(
LOG_ERROR, 
"Out of memory creating filter entry for CDR variable '%s' in table '%s' on connection '%s'\n", cdrvar, table, connection);
 
  222                entry->cdrname = (
char *)entry + 
sizeof(*entry);
 
  223                entry->filtervalue = (
char *)entry + 
sizeof(*entry) + strlen(cdrvar) + 1;
 
  224                strcpy(entry->cdrname, cdrvar);
 
  225                strcpy(entry->filtervalue, 
var->value);
 
  226                entry->negatefiltervalue = negate;
 
  232        while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
 
  233            char *cdrvar = 
"", *staticvalue = 
"";
 
  235            SQLGetData(stmt,  4, SQL_C_CHAR, columnname, 
sizeof(columnname), &sqlptr);
 
  244                if (strncmp(
var->name, 
"alias", 5) == 0 && strcasecmp(
var->value, columnname) == 0) {
 
  247                    ast_verb(3, 
"Found alias %s for column %s in %s@%s\n", cdrvar, columnname, tableptr->
table, tableptr->
connection);
 
  249                } 
else if (strncmp(
var->name, 
"static", 6) == 0 && strcasecmp(
var->value, columnname) == 0) {
 
  261            entry = 
ast_calloc(
sizeof(
char), 
sizeof(*entry) + strlen(columnname) + 1 + strlen(cdrvar) + 1 + strlen(staticvalue) + 1);
 
  263                ast_log(
LOG_ERROR, 
"Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
 
  265                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
  268            entry->
name = (
char *)entry + 
sizeof(*entry);
 
  269            strcpy(entry->
name, columnname);
 
  272                entry->
cdrname = entry->
name + strlen(columnname) + 1;
 
  273                strcpy(entry->
cdrname, cdrvar);
 
  275                entry->
cdrname = (
char *)entry + 
sizeof(*entry);
 
  283            SQLGetData(stmt,  5, SQL_C_SHORT, &entry->
type, 
sizeof(entry->
type), 
NULL);
 
  284            SQLGetData(stmt,  7, SQL_C_LONG, &entry->
size, 
sizeof(entry->
size), 
NULL);
 
  286            SQLGetData(stmt, 10, SQL_C_SHORT, &entry->
radix, 
sizeof(entry->
radix), 
NULL);
 
  296            ast_verb(4, 
"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);
 
  302        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
 
  331    SQLINTEGER nativeerror = 0, numfields = 0;
 
  332    SQLSMALLINT diagbytes = 0;
 
  333    unsigned char state[10], diagnostic[256];
 
  335    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->
con, &stmt);
 
  336    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
 
  342    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
 
  344        SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
 
  345        for (i = 0; i < numfields; i++) {
 
  346            SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, 
state, &nativeerror, diagnostic, 
sizeof(diagnostic), &diagbytes);
 
  349                ast_log(
LOG_WARNING, 
"Oh, that was good.  There are really %d diagnostics?\n", (
int)numfields);
 
  353        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
 
  360#define LENGTHEN_BUF(size, var_sql)                                                     \ 
  363                if (ast_str_strlen(var_sql) + size + 1 > ast_str_size(var_sql)) {       \ 
  364                    if (ast_str_make_space(&var_sql, ((ast_str_size(var_sql) + size + 1) / 512 + 1) * 512) != 0) { \ 
  365                        ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CDR '%s:%s' failed.\n", tableptr->connection, tableptr->table); \ 
  368                        AST_RWLIST_UNLOCK(&odbc_tables);                        \ 
 
  374#define LENGTHEN_BUF1(size) \ 
  375    LENGTHEN_BUF(size, sql); 
 
  376#define LENGTHEN_BUF2(size) \ 
  377    LENGTHEN_BUF(size, sql2); 
 
  386    char colbuf[1024], *colptr;
 
  387    SQLHSTMT stmt = 
NULL;
 
  424                ast_str_set(&sql, 0, 
"INSERT INTO %c%s%c.%c%s%c (",
 
  441            if (strcasecmp(entry->
cdrname, 
"start") == 0) {
 
  443            } 
else if (strcasecmp(entry->
cdrname, 
"answer") == 0) {
 
  445            } 
else if (strcasecmp(entry->
cdrname, 
"end") == 0) {
 
  452            } 
else if (datefield && tableptr->
usegmtime) {
 
  453                struct timeval date_tv = (datefield == 1) ? cdr->
start : (datefield == 2) ? cdr->
answer : cdr->
end;
 
  454                struct ast_tm tm = { 0, };
 
  456                ast_strftime(colbuf, 
sizeof(colbuf), 
"%Y-%m-%d %H:%M:%S", &tm);
 
  469                    ast_verb(4, 
"CDR column '%s' with value '%s' does not match filter of" 
  470                        " %s'%s'.  Cancelling this CDR.\n",
 
  481                switch (entry->
type) {
 
  484                case SQL_LONGVARCHAR:
 
  485#ifdef HAVE_ODBC_WCHAR 
  488                case SQL_WLONGVARCHAR:
 
  492                case SQL_LONGVARBINARY:
 
  497                    if (strcasecmp(entry->
name, 
"disposition") == 0) {
 
  499                    } 
else if (strcasecmp(entry->
name, 
"amaflags") == 0) {
 
  504                    if (entry->
type != SQL_GUID) {
 
  505                        if (strlen(colptr) > entry->
octetlen) {
 
  514                    for (tmp = colptr; *tmp; tmp++) {
 
  529                        int year = 0, month = 0, day = 0;
 
  530                        if (sscanf(colptr, 
"%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
 
  531                            month <= 0 || month > 12 || day < 0 || day > 31 ||
 
  532                            ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
 
  533                            (month == 2 && year % 400 == 0 && day > 29) ||
 
  534                            (month == 2 && year % 100 == 0 && day > 28) ||
 
  535                            (month == 2 && year % 4 == 0 && day > 29) ||
 
  536                            (month == 2 && year % 4 != 0 && day > 28)) {
 
  541                        if (year > 0 && year < 100) {
 
  546                        ast_str_append(&sql2, 0, 
"%s{ d '%04d-%02d-%02d' }", separator, year, month, day);
 
  553                        int hour = 0, minute = 0, second = 0;
 
  554                        int count = sscanf(colptr, 
"%2d:%2d:%2d", &hour, &minute, &second);
 
  556                        if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) {
 
  562                        ast_str_append(&sql2, 0, 
"%s{ t '%02d:%02d:%02d' }", separator, hour, minute, second);
 
  565                case SQL_TYPE_TIMESTAMP:
 
  571                        int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
 
  572                        int count = sscanf(colptr, 
"%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &minute, &second);
 
  574                        if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
 
  575                            month <= 0 || month > 12 || day < 0 || day > 31 ||
 
  576                            ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
 
  577                            (month == 2 && year % 400 == 0 && day > 29) ||
 
  578                            (month == 2 && year % 100 == 0 && day > 28) ||
 
  579                            (month == 2 && year % 4 == 0 && day > 29) ||
 
  580                            (month == 2 && year % 4 != 0 && day > 28) ||
 
  581                            hour > 23 || minute > 59 || second > 59 || hour < 0 || minute < 0 || second < 0) {
 
  586                        if (year > 0 && year < 100) {
 
  591                        ast_str_append(&sql2, 0, 
"%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", separator, year, month, day, hour, minute, second);
 
  599                        if (sscanf(colptr, 
"%30d", &
integer) != 1) {
 
  613                        if (sscanf(colptr, 
"%30lld", &
integer) != 1) {
 
  627                        if (sscanf(colptr, 
"%30hd", &
integer) != 1) {
 
  641                        if (sscanf(colptr, 
"%30hhd", &
integer) != 1) {
 
  655                        if (sscanf(colptr, 
"%30hhd", &
integer) != 1) {
 
  673                        if (!strcasecmp(entry->
cdrname, 
"billsec")) {
 
  675                                snprintf(colbuf, 
sizeof(colbuf), 
"%lf",
 
  680                        } 
else if (!strcasecmp(entry->
cdrname, 
"duration")) {
 
  681                            snprintf(colbuf, 
sizeof(colbuf), 
"%lf",
 
  689                        if (sscanf(colptr, 
"%30lf", &
number) != 1) {
 
  706                        if (!strcasecmp(entry->
cdrname, 
"billsec")) {
 
  708                                snprintf(colbuf, 
sizeof(colbuf), 
"%lf",
 
  713                        } 
else if (!strcasecmp(entry->
cdrname, 
"duration")) {
 
  714                            snprintf(colbuf, 
sizeof(colbuf), 
"%lf",
 
  722                        if (sscanf(colptr, 
"%30lf", &
number) != 1) {
 
  744                ast_verb(4, 
"CDR column '%s' was not set and does not match filter of" 
  745                    " %s'%s'.  Cancelling this CDR.\n",
 
  762            SQLRowCount(stmt, &rows);
 
  763            SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
 
  835    .
requires = 
"cdr,res_odbc",
 
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ast_calloc(num, len)
A wrapper for calloc()
void ast_cdr_format_var(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int raw)
Format a CDR variable from an already posted CDR.
int ast_cdr_unregister(const char *name)
Unregister a CDR handling engine.
int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
Register a CDR handling engine.
#define LENGTHEN_BUF2(size)
static int odbc_log(struct ast_cdr *cdr)
static int free_config(void)
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
static int load_module(void)
static int unload_module(void)
static int load_config(void)
#define LENGTHEN_BUF1(size)
General Asterisk PBX channel definitions.
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
#define ast_debug(level,...)
Log a DEBUG message.
#define ast_verb(level,...)
A set of macros to manage forward-linked lists.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
#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_RWLIST_WRLOCK(head)
Write locks a list.
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized.
#define AST_RWLIST_REMOVE_HEAD
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_RWLIST_INSERT_TAIL
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a 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).
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...
Asterisk locking-related definitions:
Asterisk module definitions.
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
@ AST_MODULE_SUPPORT_CORE
#define ASTERISK_GPL_KEY
The text the key() function should return.
int ast_odbc_prepare(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Prepares a SQL query on a statement.
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
int ast_odbc_backslash_is_escape(struct odbc_obj *obj)
Checks if the database natively supports backslash as an escape character.
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.
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
static force_inline int attribute_pure ast_strlen_zero(const char *s)
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Responsible for call detail data.
Structure used to handle boolean flags.
Support for dynamic strings.
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
unsigned int negatefiltervalue
struct columns::@103 list
struct tables::odbc_columns columns
static struct aco_type item
Time-related functions and macros.
int64_t ast_tvdiff_us(struct timeval end, struct timeval start)
Computes the difference (in microseconds) between two struct timeval instances.
int ast_tvzero(const struct timeval t)
Returns true if the argument is 0,0.