cel_odbc.c File Reference

ODBC CEL backend. More...

#include "asterisk.h"
#include <sys/types.h>
#include <time.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>
#include "asterisk/config.h"
#include "asterisk/channel.h"
#include "asterisk/lock.h"
#include "asterisk/linkedlists.h"
#include "asterisk/res_odbc.h"
#include "asterisk/cel.h"
#include "asterisk/module.h"

Include dependency graph for cel_odbc.c:

Go to the source code of this file.

Data Structures

struct  columns
struct  odbc_tables
struct  tables
struct  tables::odbc_columns

Defines

#define CEL_SHOW_USERDEF_DEFAULT   0
 show_user_def is off by default
#define CONFIG   "cel_odbc.conf"
#define LENGTHEN_BUF1(size)
#define LENGTHEN_BUF2(size)
#define ODBC_BACKEND_NAME   "ODBC CEL backend"

Functions

static void __fini_odbc_tables (void)
static void __init_odbc_tables (void)
static void __reg_module (void)
static void __unreg_module (void)
static int free_config (void)
static SQLHSTMT generic_prepare (struct odbc_obj *obj, void *data)
static int load_config (void)
static int load_module (void)
static void odbc_log (struct ast_event *event)
static int reload (void)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "ODBC CEL backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, }
static struct ast_module_infoast_module_info = &__mod_info
static unsigned char cel_show_user_def
static int maxsize = 512
static int maxsize2 = 512


Detailed Description

ODBC CEL backend.

Author:
Tilghman Lesher
<tlesher AT digium DOT com> 

Definition in file cel_odbc.c.


Define Documentation

#define CEL_SHOW_USERDEF_DEFAULT   0

show_user_def is off by default

Definition at line 58 of file cel_odbc.c.

#define CONFIG   "cel_odbc.conf"

Definition at line 53 of file cel_odbc.c.

#define LENGTHEN_BUF1 ( size   ) 

Definition at line 344 of file cel_odbc.c.

#define LENGTHEN_BUF2 ( size   ) 

Definition at line 358 of file cel_odbc.c.

#define ODBC_BACKEND_NAME   "ODBC CEL backend"

Definition at line 55 of file cel_odbc.c.

Referenced by load_module(), and unload_module().


Function Documentation

static void __fini_odbc_tables ( void   )  [static]

Definition at line 89 of file cel_odbc.c.

00092 {

static void __init_odbc_tables ( void   )  [static]

Definition at line 89 of file cel_odbc.c.

00092 {

static void __reg_module ( void   )  [static]

Definition at line 842 of file cel_odbc.c.

static void __unreg_module ( void   )  [static]

Definition at line 842 of file cel_odbc.c.

static int free_config ( void   )  [static]

Definition at line 297 of file cel_odbc.c.

References ast_free, AST_LIST_REMOVE_HEAD, AST_RWLIST_REMOVE_HEAD, and tables::columns.

00298 {
00299    struct tables *table;
00300    struct columns *entry;
00301    while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
00302       while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
00303          ast_free(entry);
00304       }
00305       ast_free(table);
00306    }
00307    return 0;
00308 }

static SQLHSTMT generic_prepare ( struct odbc_obj obj,
void *  data 
) [static]

Definition at line 310 of file cel_odbc.c.

References ast_log, odbc_obj::con, LOG_WARNING, and NULL.

00311 {
00312    int res, i;
00313    char *sql = data;
00314    SQLHSTMT stmt;
00315    SQLINTEGER nativeerror = 0, numfields = 0;
00316    SQLSMALLINT diagbytes = 0;
00317    unsigned char state[10], diagnostic[256];
00318 
00319    res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
00320    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00321       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
00322       return NULL;
00323    }
00324 
00325    res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
00326    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00327       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
00328       SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
00329       for (i = 0; i < numfields; i++) {
00330          SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
00331          ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
00332          if (i > 10) {
00333             ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
00334             break;
00335          }
00336       }
00337       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
00338       return NULL;
00339    }
00340 
00341    return stmt;
00342 }

static int load_config ( void   )  [static]

Definition at line 91 of file cel_odbc.c.

References tables::allowleapsec, ast_calloc, ast_category_browse(), ast_config_load, ast_copy_string(), ast_free, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, ast_log, ast_odbc_release_obj(), ast_odbc_request_obj, AST_RWLIST_INSERT_TAIL, ast_strdupa, ast_strip(), ast_strlen_zero, ast_true(), ast_variable_browse(), ast_variable_retrieve(), ast_verb, CEL_SHOW_USERDEF_DEFAULT, columns::celname, tables::columns, odbc_obj::con, CONFIG, CONFIG_STATUS_FILEINVALID, tables::connection, columns::decimals, columns::filtervalue, item, LOG_ERROR, LOG_NOTICE, LOG_WARNING, columns::name, ast_variable::name, ast_variable::next, NULL, columns::nullable, columns::octetlen, columns::radix, columns::size, columns::staticvalue, tables::table, tmp(), columns::type, tables::usegmtime, ast_variable::value, and var.

00092 {
00093    struct ast_config *cfg;
00094    struct ast_variable *var;
00095    const char *tmp, *catg;
00096    struct tables *tableptr;
00097    struct columns *entry;
00098    struct odbc_obj *obj;
00099    char columnname[80];
00100    char connection[40];
00101    char table[40];
00102    int lenconnection, lentable;
00103    SQLLEN sqlptr;
00104    int res = 0;
00105    SQLHSTMT stmt = NULL;
00106    struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */
00107 
00108    cfg = ast_config_load(CONFIG, config_flags);
00109    if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
00110       ast_log(LOG_WARNING, "Unable to load " CONFIG ".  No ODBC CEL records!\n");
00111       return -1;
00112    }
00113 
00114    /* Process the general category */
00115    cel_show_user_def = CEL_SHOW_USERDEF_DEFAULT;
00116    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
00117       if (!strcasecmp(var->name, "show_user_defined")) {
00118          cel_show_user_def = ast_true(var->value) ? 1 : 0;
00119       } else {
00120          /* Unknown option name. */
00121       }
00122    }
00123 
00124    for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
00125       if (!strcasecmp(catg, "general")) {
00126          continue;
00127       }
00128       var = ast_variable_browse(cfg, catg);
00129       if (!var)
00130          continue;
00131 
00132       if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
00133          ast_log(LOG_WARNING, "No connection parameter found in '%s'.  Skipping.\n", catg);
00134          continue;
00135       }
00136       ast_copy_string(connection, tmp, sizeof(connection));
00137       lenconnection = strlen(connection);
00138 
00139       /* When loading, we want to be sure we can connect. */
00140       obj = ast_odbc_request_obj(connection, 1);
00141       if (!obj) {
00142          ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ".  Check res_odbc.conf.\n", connection, catg);
00143          continue;
00144       }
00145 
00146       if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
00147          ast_log(LOG_NOTICE, "No table name found.  Assuming 'cel'.\n");
00148          tmp = "cel";
00149       }
00150       ast_copy_string(table, tmp, sizeof(table));
00151       lentable = strlen(table);
00152 
00153       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
00154       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00155          ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
00156          ast_odbc_release_obj(obj);
00157          continue;
00158       }
00159 
00160       res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
00161       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
00162          ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.  Skipping.\n", connection);
00163          ast_odbc_release_obj(obj);
00164          continue;
00165       }
00166 
00167       tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
00168       if (!tableptr) {
00169          ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
00170          ast_odbc_release_obj(obj);
00171          res = -1;
00172          break;
00173       }
00174 
00175       tableptr->connection = (char *)tableptr + sizeof(*tableptr);
00176       tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
00177       ast_copy_string(tableptr->connection, connection, lenconnection + 1);
00178       ast_copy_string(tableptr->table, table, lentable + 1);
00179 
00180       tableptr->usegmtime = 0;
00181       if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) {
00182          tableptr->usegmtime = ast_true(tmp);
00183       }
00184 
00185       tableptr->allowleapsec = 1;
00186       if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "allowleapsecond"))) {
00187          tableptr->allowleapsec = ast_true(tmp);
00188       }
00189 
00190       ast_verb(3, "Found CEL table %s@%s.\n", tableptr->table, tableptr->connection);
00191 
00192       /* Check for filters first */
00193       for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
00194          if (strncmp(var->name, "filter", 6) == 0) {
00195             char *celvar = ast_strdupa(var->name + 6);
00196             celvar = ast_strip(celvar);
00197             ast_verb(3, "Found filter %s for cel variable %s in %s@%s\n", var->value, celvar, tableptr->table, tableptr->connection);
00198 
00199             entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(celvar) + 1 + strlen(var->value) + 1);
00200             if (!entry) {
00201                ast_log(LOG_ERROR, "Out of memory creating filter entry for CEL variable '%s' in table '%s' on connection '%s'\n", celvar, table, connection);
00202                res = -1;
00203                break;
00204             }
00205 
00206             /* NULL column entry means this isn't a column in the database */
00207             entry->name = NULL;
00208             entry->celname = (char *)entry + sizeof(*entry);
00209             entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(celvar) + 1;
00210             strcpy(entry->celname, celvar);
00211             strcpy(entry->filtervalue, var->value);
00212 
00213             AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00214          }
00215       }
00216 
00217       while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
00218          char *celvar = "", *staticvalue = "";
00219 
00220          SQLGetData(stmt,  4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
00221 
00222          /* Is there an alias for this column? */
00223 
00224          /* NOTE: This seems like a non-optimal parse method, but I'm going
00225           * for user configuration readability, rather than fast parsing. We
00226           * really don't parse this file all that often, anyway.
00227           */
00228          for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
00229             if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
00230                char *alias = ast_strdupa(var->name + 5);
00231                celvar = ast_strip(alias);
00232                ast_verb(3, "Found alias %s for column %s in %s@%s\n", celvar, columnname, tableptr->table, tableptr->connection);
00233                break;
00234             } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) {
00235                char *item = ast_strdupa(var->name + 6);
00236                item = ast_strip(item);
00237                if (item[0] == '"' && item[strlen(item) - 1] == '"') {
00238                   /* Remove surrounding quotes */
00239                   item[strlen(item) - 1] = '\0';
00240                   item++;
00241                }
00242                staticvalue = item;
00243             }
00244          }
00245 
00246          entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(celvar) + 1 + strlen(staticvalue) + 1);
00247          if (!entry) {
00248             ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
00249             res = -1;
00250             break;
00251          }
00252          entry->name = (char *)entry + sizeof(*entry);
00253          strcpy(entry->name, columnname);
00254 
00255          if (!ast_strlen_zero(celvar)) {
00256             entry->celname = entry->name + strlen(columnname) + 1;
00257             strcpy(entry->celname, celvar);
00258          } else { /* Point to same place as the column name */
00259             entry->celname = (char *)entry + sizeof(*entry);
00260          }
00261 
00262          if (!ast_strlen_zero(staticvalue)) {
00263             entry->staticvalue = entry->celname + strlen(entry->celname) + 1;
00264             strcpy(entry->staticvalue, staticvalue);
00265          }
00266 
00267          SQLGetData(stmt,  5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
00268          SQLGetData(stmt,  7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
00269          SQLGetData(stmt,  9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
00270          SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
00271          SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
00272          SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
00273 
00274          /* Specification states that the octenlen should be the maximum number of bytes
00275           * returned in a char or binary column, but it seems that some drivers just set
00276           * it to NULL. (Bad Postgres! No biscuit!) */
00277          if (entry->octetlen == 0)
00278             entry->octetlen = entry->size;
00279 
00280          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);
00281          /* Insert column info into column list */
00282          AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
00283          res = 0;
00284       }
00285 
00286       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00287       ast_odbc_release_obj(obj);
00288 
00289       if (AST_LIST_FIRST(&(tableptr->columns)))
00290          AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
00291       else
00292          ast_free(tableptr);
00293    }
00294    return res;
00295 }

static int load_module ( void   )  [static]

Definition at line 806 of file cel_odbc.c.

References ast_cel_backend_register(), ast_log, AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, AST_RWLIST_HEAD_INIT, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, load_config(), LOG_ERROR, ODBC_BACKEND_NAME, and odbc_log().

00807 {
00808    AST_RWLIST_HEAD_INIT(&odbc_tables);
00809 
00810    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00811       ast_log(LOG_ERROR, "Unable to lock column list.  Load failed.\n");
00812       return AST_MODULE_LOAD_FAILURE;
00813    }
00814    load_config();
00815    AST_RWLIST_UNLOCK(&odbc_tables);
00816    if (ast_cel_backend_register(ODBC_BACKEND_NAME, odbc_log)) {
00817       ast_log(LOG_ERROR, "Unable to subscribe to CEL events\n");
00818       return AST_MODULE_LOAD_FAILURE;
00819    }
00820    return AST_MODULE_LOAD_SUCCESS;
00821 }

static void odbc_log ( struct ast_event event  )  [static]

Definition at line 371 of file cel_odbc.c.

References ast_cel_event_record::account_code, tables::allowleapsec, ast_cel_event_record::amaflag, ast_cel_event_record::application_data, ast_cel_event_record::application_name, AST_CEL_EVENT_RECORD_VERSION, ast_cel_fill_record(), AST_CEL_USER_DEFINED, ast_copy_string(), ast_debug, ast_free, AST_LIST_TRAVERSE, ast_localtime(), ast_log, ast_odbc_backslash_is_escape(), ast_odbc_prepare_and_execute(), ast_odbc_release_obj(), ast_odbc_request_obj, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_str_strlen(), ast_strdupa, ast_strftime(), ast_strlen_zero, ast_verb, ast_cel_event_record::caller_id_ani, ast_cel_event_record::caller_id_dnid, ast_cel_event_record::caller_id_name, ast_cel_event_record::caller_id_num, ast_cel_event_record::caller_id_rdnis, columns::celname, ast_cel_event_record::channel_name, tables::columns, tables::connection, ast_cel_event_record::context, columns::decimals, ast_cel_event_record::event_name, ast_cel_event_record::event_time, ast_cel_event_record::event_type, ast_cel_event_record::extension, ast_cel_event_record::extra, columns::filtervalue, columns::first, generic_prepare(), LENGTHEN_BUF1, LENGTHEN_BUF2, ast_cel_event_record::linked_id, LOG_ERROR, LOG_WARNING, maxsize2, columns::name, NULL, columns::octetlen, ast_cel_event_record::peer, ast_cel_event_record::peer_account, columns::radix, columns::staticvalue, tables::table, ast_tm::tm_hour, ast_tm::tm_mday, ast_tm::tm_min, ast_tm::tm_mon, ast_tm::tm_sec, ast_tm::tm_usec, ast_tm::tm_year, tmp(), columns::type, ast_cel_event_record::unique_id, unknown, tables::usegmtime, ast_cel_event_record::user_defined_name, ast_cel_event_record::user_field, and ast_cel_event_record::version.

00372 {
00373    struct tables *tableptr;
00374    struct columns *entry;
00375    struct odbc_obj *obj;
00376    struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
00377    char *tmp;
00378    char colbuf[1024], *colptr;
00379    SQLHSTMT stmt = NULL;
00380    SQLLEN rows = 0;
00381    struct ast_cel_event_record record = {
00382       .version = AST_CEL_EVENT_RECORD_VERSION,
00383    };
00384 
00385    if (ast_cel_fill_record(event, &record)) {
00386       return;
00387    }
00388 
00389    if (!sql || !sql2) {
00390       if (sql)
00391          ast_free(sql);
00392       if (sql2)
00393          ast_free(sql2);
00394       return;
00395    }
00396 
00397    if (AST_RWLIST_RDLOCK(&odbc_tables)) {
00398       ast_log(LOG_ERROR, "Unable to lock table list.  Insert CEL(s) failed.\n");
00399       ast_free(sql);
00400       ast_free(sql2);
00401       return;
00402    }
00403 
00404    AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
00405       int first = 1;
00406       ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
00407       ast_str_set(&sql2, 0, " VALUES (");
00408 
00409       /* No need to check the connection now; we'll handle any failure in prepare_and_execute */
00410       if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
00411          ast_log(LOG_WARNING, "Unable to retrieve database handle for '%s:%s'.  CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
00412          continue;
00413       }
00414 
00415       AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
00416          int datefield = 0;
00417          int unknown = 0;
00418          if (strcasecmp(entry->celname, "eventtime") == 0) {
00419             datefield = 1;
00420          }
00421 
00422          /* Check if we have a similarly named variable */
00423          if (entry->staticvalue) {
00424             colptr = ast_strdupa(entry->staticvalue);
00425          } else if (datefield) {
00426             struct timeval date_tv = record.event_time;
00427             struct ast_tm tm = { 0, };
00428             ast_localtime(&date_tv, &tm, tableptr->usegmtime ? "UTC" : NULL);
00429             /* SQL server 2008 added datetime2 and datetimeoffset data types, that
00430                are reported to SQLColumns() as SQL_WVARCHAR, according to "Enhanced
00431                Date/Time Type Behavior with Previous SQL Server Versions (ODBC)".
00432                Here we format the event time with fraction seconds, so these new
00433                column types will be set to high-precision event time. However, 'date'
00434                and 'time' columns, also newly introduced, reported as SQL_WVARCHAR
00435                too, and insertion of the value formatted here into these will fail.
00436                This should be ok, however, as nobody is going to store just event
00437                date or just time for CDR purposes.
00438              */
00439             ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S.%6q", &tm);
00440             colptr = colbuf;
00441          } else {
00442             if (strcmp(entry->celname, "userdeftype") == 0) {
00443                ast_copy_string(colbuf, record.user_defined_name, sizeof(colbuf));
00444             } else if (strcmp(entry->celname, "cid_name") == 0) {
00445                ast_copy_string(colbuf, record.caller_id_name, sizeof(colbuf));
00446             } else if (strcmp(entry->celname, "cid_num") == 0) {
00447                ast_copy_string(colbuf, record.caller_id_num, sizeof(colbuf));
00448             } else if (strcmp(entry->celname, "cid_ani") == 0) {
00449                ast_copy_string(colbuf, record.caller_id_ani, sizeof(colbuf));
00450             } else if (strcmp(entry->celname, "cid_rdnis") == 0) {
00451                ast_copy_string(colbuf, record.caller_id_rdnis, sizeof(colbuf));
00452             } else if (strcmp(entry->celname, "cid_dnid") == 0) {
00453                ast_copy_string(colbuf, record.caller_id_dnid, sizeof(colbuf));
00454             } else if (strcmp(entry->celname, "exten") == 0) {
00455                ast_copy_string(colbuf, record.extension, sizeof(colbuf));
00456             } else if (strcmp(entry->celname, "context") == 0) {
00457                ast_copy_string(colbuf, record.context, sizeof(colbuf));
00458             } else if (strcmp(entry->celname, "channame") == 0) {
00459                ast_copy_string(colbuf, record.channel_name, sizeof(colbuf));
00460             } else if (strcmp(entry->celname, "appname") == 0) {
00461                ast_copy_string(colbuf, record.application_name, sizeof(colbuf));
00462             } else if (strcmp(entry->celname, "appdata") == 0) {
00463                ast_copy_string(colbuf, record.application_data, sizeof(colbuf));
00464             } else if (strcmp(entry->celname, "accountcode") == 0) {
00465                ast_copy_string(colbuf, record.account_code, sizeof(colbuf));
00466             } else if (strcmp(entry->celname, "peeraccount") == 0) {
00467                ast_copy_string(colbuf, record.peer_account, sizeof(colbuf));
00468             } else if (strcmp(entry->celname, "uniqueid") == 0) {
00469                ast_copy_string(colbuf, record.unique_id, sizeof(colbuf));
00470             } else if (strcmp(entry->celname, "linkedid") == 0) {
00471                ast_copy_string(colbuf, record.linked_id, sizeof(colbuf));
00472             } else if (strcmp(entry->celname, "userfield") == 0) {
00473                ast_copy_string(colbuf, record.user_field, sizeof(colbuf));
00474             } else if (strcmp(entry->celname, "peer") == 0) {
00475                ast_copy_string(colbuf, record.peer, sizeof(colbuf));
00476             } else if (strcmp(entry->celname, "amaflags") == 0) {
00477                snprintf(colbuf, sizeof(colbuf), "%u", record.amaflag);
00478             } else if (strcmp(entry->celname, "extra") == 0) {
00479                ast_copy_string(colbuf, record.extra, sizeof(colbuf));
00480             } else if (strcmp(entry->celname, "eventtype") == 0) {
00481                snprintf(colbuf, sizeof(colbuf), "%u", record.event_type);
00482             } else {
00483                colbuf[0] = 0;
00484                unknown = 1;
00485             }
00486             colptr = colbuf;
00487          }
00488 
00489          if (colptr && !unknown) {
00490             /* Check first if the column filters this entry.  Note that this
00491              * is very specifically NOT ast_strlen_zero(), because the filter
00492              * could legitimately specify that the field is blank, which is
00493              * different from the field being unspecified (NULL). */
00494             if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
00495                ast_verb(4, "CEL column '%s' with value '%s' does not match filter of"
00496                   " '%s'.  Cancelling this CEL.\n",
00497                   entry->celname, colptr, entry->filtervalue);
00498                goto early_release;
00499             }
00500 
00501             /* Only a filter? */
00502             if (ast_strlen_zero(entry->name))
00503                continue;
00504 
00505             LENGTHEN_BUF1(strlen(entry->name));
00506 
00507             switch (entry->type) {
00508             case SQL_CHAR:
00509             case SQL_VARCHAR:
00510             case SQL_LONGVARCHAR:
00511 #ifdef HAVE_ODBC_WCHAR
00512             case SQL_WCHAR:
00513             case SQL_WVARCHAR:
00514             case SQL_WLONGVARCHAR:
00515 #endif
00516             case SQL_BINARY:
00517             case SQL_VARBINARY:
00518             case SQL_LONGVARBINARY:
00519             case SQL_GUID:
00520                /* For these two field names, get the rendered form, instead of the raw
00521                 * form (but only when we're dealing with a character-based field).
00522                 */
00523                if (strcasecmp(entry->name, "eventtype") == 0) {
00524                   const char *event_name;
00525 
00526                   event_name = (!cel_show_user_def
00527                      && record.event_type == AST_CEL_USER_DEFINED)
00528                      ? record.user_defined_name : record.event_name;
00529                   snprintf(colbuf, sizeof(colbuf), "%s", event_name);
00530                }
00531 
00532                /* Truncate too-long fields */
00533                if (entry->type != SQL_GUID) {
00534                   if (strlen(colptr) > entry->octetlen) {
00535                      colptr[entry->octetlen] = '\0';
00536                   }
00537                }
00538 
00539                ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00540                LENGTHEN_BUF2(strlen(colptr));
00541 
00542                /* Encode value, with escaping */
00543                ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
00544                for (tmp = colptr; *tmp; tmp++) {
00545                   if (*tmp == '\'') {
00546                      ast_str_append(&sql2, 0, "''");
00547                   } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
00548                      ast_str_append(&sql2, 0, "\\\\");
00549                   } else {
00550                      ast_str_append(&sql2, 0, "%c", *tmp);
00551                   }
00552                }
00553                ast_str_append(&sql2, 0, "'");
00554                break;
00555             case SQL_TYPE_DATE:
00556                if (ast_strlen_zero(colptr)) {
00557                   continue;
00558                } else {
00559                   int year = 0, month = 0, day = 0;
00560                   if (strcasecmp(entry->name, "eventdate") == 0) {
00561                      struct ast_tm tm;
00562                      ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
00563                      year = tm.tm_year + 1900;
00564                      month = tm.tm_mon + 1;
00565                      day = tm.tm_mday;
00566                   } else {
00567                      if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
00568                         month <= 0 || month > 12 || day < 0 || day > 31 ||
00569                         ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00570                         (month == 2 && year % 400 == 0 && day > 29) ||
00571                         (month == 2 && year % 100 == 0 && day > 28) ||
00572                         (month == 2 && year % 4 == 0 && day > 29) ||
00573                         (month == 2 && year % 4 != 0 && day > 28)) {
00574                         ast_log(LOG_WARNING, "CEL variable %s is not a valid date ('%s').\n", entry->name, colptr);
00575                         continue;
00576                      }
00577 
00578                      if (year > 0 && year < 100) {
00579                         year += 2000;
00580                      }
00581                   }
00582 
00583                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00584                   LENGTHEN_BUF2(17);
00585                   ast_str_append(&sql2, 0, "%s{d '%04d-%02d-%02d'}", first ? "" : ",", year, month, day);
00586                }
00587                break;
00588             case SQL_TYPE_TIME:
00589                if (ast_strlen_zero(colptr)) {
00590                   continue;
00591                } else {
00592                   int hour = 0, minute = 0, second = 0;
00593                   if (strcasecmp(entry->name, "eventdate") == 0) {
00594                      struct ast_tm tm;
00595                      ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
00596                      hour = tm.tm_hour;
00597                      minute = tm.tm_min;
00598                      second = (tableptr->allowleapsec || tm.tm_sec < 60) ? tm.tm_sec : 59;
00599                   } else {
00600                      int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
00601 
00602                      if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > (tableptr->allowleapsec ? 60 : 59)) {
00603                         ast_log(LOG_WARNING, "CEL variable %s is not a valid time ('%s').\n", entry->name, colptr);
00604                         continue;
00605                      }
00606                   }
00607 
00608                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00609                   LENGTHEN_BUF2(15);
00610                   ast_str_append(&sql2, 0, "%s{t '%02d:%02d:%02d'}", first ? "" : ",", hour, minute, second);
00611                }
00612                break;
00613             case SQL_TYPE_TIMESTAMP:
00614             case SQL_TIMESTAMP:
00615                if (ast_strlen_zero(colptr)) {
00616                   continue;
00617                } else {
00618                   int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, fraction = 0;
00619                   if (strcasecmp(entry->name, "eventdate") == 0) {
00620                      struct ast_tm tm;
00621                      ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
00622                      year = tm.tm_year + 1900;
00623                      month = tm.tm_mon + 1;
00624                      day = tm.tm_mday;
00625                      hour = tm.tm_hour;
00626                      minute = tm.tm_min;
00627                      second = (tableptr->allowleapsec || tm.tm_sec < 60) ? tm.tm_sec : 59;
00628                      fraction = tm.tm_usec;
00629                   } else {
00630                      int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%2d.%6d", &year, &month, &day, &hour, &minute, &second, &fraction);
00631 
00632                      if ((count != 3 && count != 5 && count != 6 && count != 7) || year <= 0 ||
00633                         month <= 0 || month > 12 || day < 0 || day > 31 ||
00634                         ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
00635                         (month == 2 && year % 400 == 0 && day > 29) ||
00636                         (month == 2 && year % 100 == 0 && day > 28) ||
00637                         (month == 2 && year % 4 == 0 && day > 29) ||
00638                         (month == 2 && year % 4 != 0 && day > 28) ||
00639                         hour > 23 || minute > 59 || second > (tableptr->allowleapsec ? 60 : 59) || hour < 0 || minute < 0 || second < 0 || fraction < 0) {
00640                         ast_log(LOG_WARNING, "CEL variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
00641                         continue;
00642                      }
00643 
00644                      if (year > 0 && year < 100) {
00645                         year += 2000;
00646                      }
00647                   }
00648 
00649                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00650                   LENGTHEN_BUF2(27);
00651                   ast_str_append(&sql2, 0, "%s{ts '%04d-%02d-%02d %02d:%02d:%02d.%d'}", first ? "" : ",", year, month, day, hour, minute, second, fraction);
00652                }
00653                break;
00654             case SQL_INTEGER:
00655                {
00656                   int integer = 0;
00657                   if (sscanf(colptr, "%30d", &integer) != 1) {
00658                      ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
00659                      continue;
00660                   }
00661 
00662                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00663                   LENGTHEN_BUF2(12);
00664                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00665                }
00666                break;
00667             case SQL_BIGINT:
00668                {
00669                   long long integer = 0;
00670                   int ret;
00671                   if ((ret = sscanf(colptr, "%30lld", &integer)) != 1) {
00672                      ast_log(LOG_WARNING, "CEL variable %s is not an integer. (%d - '%s')\n", entry->name, ret, colptr);
00673                      continue;
00674                   }
00675 
00676                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00677                   LENGTHEN_BUF2(24);
00678                   ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
00679                }
00680                break;
00681             case SQL_SMALLINT:
00682                {
00683                   short integer = 0;
00684                   if (sscanf(colptr, "%30hd", &integer) != 1) {
00685                      ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
00686                      continue;
00687                   }
00688 
00689                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00690                   LENGTHEN_BUF2(7);
00691                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00692                }
00693                break;
00694             case SQL_TINYINT:
00695                {
00696                   signed char integer = 0;
00697                   if (sscanf(colptr, "%30hhd", &integer) != 1) {
00698                      ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
00699                      continue;
00700                   }
00701 
00702                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00703                   LENGTHEN_BUF2(4);
00704                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00705                }
00706                break;
00707             case SQL_BIT:
00708                {
00709                   signed char integer = 0;
00710                   if (sscanf(colptr, "%30hhd", &integer) != 1) {
00711                      ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
00712                      continue;
00713                   }
00714                   if (integer != 0)
00715                      integer = 1;
00716 
00717                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00718                   LENGTHEN_BUF2(2);
00719                   ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
00720                }
00721                break;
00722             case SQL_NUMERIC:
00723             case SQL_DECIMAL:
00724                {
00725                   double number = 0.0;
00726                   if (sscanf(colptr, "%30lf", &number) != 1) {
00727                      ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);
00728                      continue;
00729                   }
00730 
00731                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00732                   LENGTHEN_BUF2(entry->decimals + 2);
00733                   ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
00734                }
00735                break;
00736             case SQL_FLOAT:
00737             case SQL_REAL:
00738             case SQL_DOUBLE:
00739                {
00740                   double number = 0.0;
00741                   if (sscanf(colptr, "%30lf", &number) != 1) {
00742                      ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);
00743                      continue;
00744                   }
00745 
00746                   ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
00747                   LENGTHEN_BUF2(entry->decimals);
00748                   ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
00749                }
00750                break;
00751             default:
00752                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);
00753                continue;
00754             }
00755             first = 0;
00756          }
00757       }
00758 
00759       /* Concatenate the two constructed buffers */
00760       LENGTHEN_BUF1(ast_str_strlen(sql2));
00761       ast_str_append(&sql, 0, ")");
00762       ast_str_append(&sql2, 0, ")");
00763       ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
00764 
00765       ast_debug(3, "Executing SQL statement: [%s]\n", ast_str_buffer(sql));
00766       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
00767       if (stmt) {
00768          SQLRowCount(stmt, &rows);
00769          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
00770       }
00771       if (rows == 0) {
00772          ast_log(LOG_WARNING, "Insert failed on '%s:%s'.  CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
00773       }
00774 early_release:
00775       ast_odbc_release_obj(obj);
00776    }
00777    AST_RWLIST_UNLOCK(&odbc_tables);
00778 
00779    /* Next time, just allocate buffers that are that big to start with. */
00780    if (ast_str_strlen(sql) > maxsize) {
00781       maxsize = ast_str_strlen(sql);
00782    }
00783    if (ast_str_strlen(sql2) > maxsize2) {
00784       maxsize2 = ast_str_strlen(sql2);
00785    }
00786 
00787    ast_free(sql);
00788    ast_free(sql2);
00789 }

static int reload ( void   )  [static]

Definition at line 823 of file cel_odbc.c.

References ast_log, AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), load_config(), and LOG_ERROR.

00824 {
00825    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00826       ast_log(LOG_ERROR, "Unable to lock column list.  Reload failed.\n");
00827       return AST_MODULE_LOAD_FAILURE;
00828    }
00829 
00830    free_config();
00831    load_config();
00832    AST_RWLIST_UNLOCK(&odbc_tables);
00833    return AST_MODULE_LOAD_SUCCESS;
00834 }

static int unload_module ( void   )  [static]

Definition at line 791 of file cel_odbc.c.

References ast_cel_backend_unregister(), ast_log, AST_RWLIST_HEAD_DESTROY, AST_RWLIST_UNLOCK, AST_RWLIST_WRLOCK, free_config(), LOG_ERROR, and ODBC_BACKEND_NAME.

00792 {
00793    if (AST_RWLIST_WRLOCK(&odbc_tables)) {
00794       ast_log(LOG_ERROR, "Unable to lock column list.  Unload failed.\n");
00795       return -1;
00796    }
00797 
00798    ast_cel_backend_unregister(ODBC_BACKEND_NAME);
00799    free_config();
00800    AST_RWLIST_UNLOCK(&odbc_tables);
00801    AST_RWLIST_HEAD_DESTROY(&odbc_tables);
00802         
00803    return 0;
00804 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "ODBC CEL backend" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CDR_DRIVER, } [static]

Definition at line 842 of file cel_odbc.c.

Definition at line 842 of file cel_odbc.c.

unsigned char cel_show_user_def [static]

TRUE if we should set the eventtype field to USER_DEFINED on user events.

Definition at line 61 of file cel_odbc.c.

int maxsize = 512 [static]

Definition at line 64 of file cel_odbc.c.

int maxsize2 = 512 [static]

Definition at line 64 of file cel_odbc.c.


Generated on Thu Apr 16 06:29:28 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6