53#define CONFIG "cel_odbc.conf"
55#define ODBC_BACKEND_NAME "ODBC CEL backend"
58#define CEL_SHOW_USERDEF_DEFAULT 0
95 const char *tmp, *catg;
102 int lenconnection, lentable;
105 SQLHSTMT stmt =
NULL;
117 if (!strcasecmp(
var->name,
"show_user_defined")) {
125 if (!strcasecmp(catg,
"general")) {
137 lenconnection = strlen(connection);
142 ast_log(
LOG_WARNING,
"No such connection '%s' in the '%s' section of " CONFIG ". Check res_odbc.conf.\n", connection, catg);
151 lentable = strlen(table);
153 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->
con, &stmt);
154 if (!SQL_SUCCEEDED(res)) {
160 res = SQLColumns(stmt,
NULL, 0,
NULL, 0, (
unsigned char *)table, SQL_NTS, (
unsigned char *)
"%", SQL_NTS);
161 if (!SQL_SUCCEEDED(res)) {
162 ast_log(
LOG_ERROR,
"Unable to query database columns on connection '%s'. Skipping.\n", connection);
167 tableptr =
ast_calloc(
sizeof(
char),
sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
169 ast_log(
LOG_ERROR,
"Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
175 tableptr->
connection = (
char *)tableptr +
sizeof(*tableptr);
176 tableptr->
table = (
char *)tableptr +
sizeof(*tableptr) + lenconnection + 1;
194 if (strncmp(
var->name,
"filter", 6) == 0) {
199 entry =
ast_calloc(
sizeof(
char),
sizeof(*entry) + strlen(celvar) + 1 + strlen(
var->value) + 1);
201 ast_log(
LOG_ERROR,
"Out of memory creating filter entry for CEL variable '%s' in table '%s' on connection '%s'\n", celvar, table, connection);
208 entry->
celname = (
char *)entry +
sizeof(*entry);
209 entry->
filtervalue = (
char *)entry +
sizeof(*entry) + strlen(celvar) + 1;
210 strcpy(entry->
celname, celvar);
217 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
218 char *celvar =
"", *staticvalue =
"";
220 SQLGetData(stmt, 4, SQL_C_CHAR, columnname,
sizeof(columnname), &sqlptr);
229 if (strncmp(
var->name,
"alias", 5) == 0 && strcasecmp(
var->value, columnname) == 0) {
232 ast_verb(3,
"Found alias %s for column %s in %s@%s\n", celvar, columnname, tableptr->
table, tableptr->
connection);
234 }
else if (strncmp(
var->name,
"static", 6) == 0 && strcasecmp(
var->value, columnname) == 0) {
246 entry =
ast_calloc(
sizeof(
char),
sizeof(*entry) + strlen(columnname) + 1 + strlen(celvar) + 1 + strlen(staticvalue) + 1);
248 ast_log(
LOG_ERROR,
"Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
252 entry->
name = (
char *)entry +
sizeof(*entry);
253 strcpy(entry->
name, columnname);
256 entry->
celname = entry->
name + strlen(columnname) + 1;
257 strcpy(entry->
celname, celvar);
259 entry->
celname = (
char *)entry +
sizeof(*entry);
267 SQLGetData(stmt, 5, SQL_C_SHORT, &entry->
type,
sizeof(entry->
type),
NULL);
268 SQLGetData(stmt, 7, SQL_C_LONG, &entry->
size,
sizeof(entry->
size),
NULL);
270 SQLGetData(stmt, 10, SQL_C_SHORT, &entry->
radix,
sizeof(entry->
radix),
NULL);
280 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);
286 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
311#define LENGTHEN_BUF(size, var_sql) \
314 if (ast_str_strlen(var_sql) + size + 1 > ast_str_size(var_sql)) { \
315 if (ast_str_make_space(&var_sql, ((ast_str_size(var_sql) + size + 1) / 512 + 1) * 512) != 0) { \
316 ast_log(LOG_ERROR, "Unable to allocate sufficient memory. Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
319 AST_RWLIST_UNLOCK(&odbc_tables); \
325#define LENGTHEN_BUF1(size) \
326 LENGTHEN_BUF(size, sql);
328#define LENGTHEN_BUF2(size) \
329 LENGTHEN_BUF(size, sql2);
338 char colbuf[1024], *colptr;
339 SQLHSTMT stmt =
NULL;
365 char *separator =
"";
378 if (strcasecmp(entry->
celname,
"eventtime") == 0) {
385 }
else if (datefield) {
387 struct ast_tm tm = { 0, };
399 ast_strftime(colbuf,
sizeof(colbuf),
"%Y-%m-%d %H:%M:%S.%6q", &tm);
402 if (strcmp(entry->
celname,
"userdeftype") == 0) {
404 }
else if (strcmp(entry->
celname,
"cid_name") == 0) {
406 }
else if (strcmp(entry->
celname,
"cid_num") == 0) {
408 }
else if (strcmp(entry->
celname,
"cid_ani") == 0) {
410 }
else if (strcmp(entry->
celname,
"cid_rdnis") == 0) {
412 }
else if (strcmp(entry->
celname,
"cid_dnid") == 0) {
414 }
else if (strcmp(entry->
celname,
"exten") == 0) {
416 }
else if (strcmp(entry->
celname,
"context") == 0) {
418 }
else if (strcmp(entry->
celname,
"channame") == 0) {
420 }
else if (strcmp(entry->
celname,
"appname") == 0) {
422 }
else if (strcmp(entry->
celname,
"appdata") == 0) {
424 }
else if (strcmp(entry->
celname,
"accountcode") == 0) {
426 }
else if (strcmp(entry->
celname,
"peeraccount") == 0) {
428 }
else if (strcmp(entry->
celname,
"uniqueid") == 0) {
430 }
else if (strcmp(entry->
celname,
"linkedid") == 0) {
432 }
else if (strcmp(entry->
celname,
"userfield") == 0) {
434 }
else if (strcmp(entry->
celname,
"peer") == 0) {
436 }
else if (strcmp(entry->
celname,
"amaflags") == 0) {
437 snprintf(colbuf,
sizeof(colbuf),
"%u", record.
amaflag);
438 }
else if (strcmp(entry->
celname,
"extra") == 0) {
440 }
else if (strcmp(entry->
celname,
"eventtype") == 0) {
441 snprintf(colbuf,
sizeof(colbuf),
"%u", record.
event_type);
455 ast_verb(4,
"CEL column '%s' with value '%s' does not match filter of"
456 " '%s'. Cancelling this CEL.\n",
467 switch (entry->
type) {
470 case SQL_LONGVARCHAR:
471#ifdef HAVE_ODBC_WCHAR
474 case SQL_WLONGVARCHAR:
478 case SQL_LONGVARBINARY:
483 if (strcasecmp(entry->
name,
"eventtype") == 0) {
484 const char *event_name;
489 snprintf(colbuf,
sizeof(colbuf),
"%s", event_name);
493 if (entry->
type != SQL_GUID) {
494 if (strlen(colptr) > entry->
octetlen) {
504 for (tmp = colptr; *tmp; tmp++) {
519 int year = 0, month = 0, day = 0;
520 if (strcasecmp(entry->
name,
"eventdate") == 0) {
527 if (sscanf(colptr,
"%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
528 month <= 0 || month > 12 || day < 0 || day > 31 ||
529 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
530 (month == 2 && year % 400 == 0 && day > 29) ||
531 (month == 2 && year % 100 == 0 && day > 28) ||
532 (month == 2 && year % 4 == 0 && day > 29) ||
533 (month == 2 && year % 4 != 0 && day > 28)) {
538 if (year > 0 && year < 100) {
545 ast_str_append(&sql2, 0,
"%s{d '%04d-%02d-%02d'}", separator, year, month, day);
552 int hour = 0, minute = 0, second = 0;
553 if (strcasecmp(entry->
name,
"eventdate") == 0) {
560 int count = sscanf(colptr,
"%2d:%2d:%2d", &hour, &minute, &second);
562 if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > (tableptr->
allowleapsec ? 60 : 59)) {
570 ast_str_append(&sql2, 0,
"%s{t '%02d:%02d:%02d'}", separator, hour, minute, second);
573 case SQL_TYPE_TIMESTAMP:
588 int year = 0, month = 0, day = 0, hour = 0, minute = 0;
591 if (strcasecmp(entry->
name,
"eventdate") == 0) {
605 second += (tm.
tm_usec / 1000000.0);
611 int count = sscanf(colptr,
"%4d-%2d-%2d %2d:%2d:%lf", &year, &month, &day, &hour, &minute, &second);
613 if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
614 month <= 0 || month > 12 || day < 0 || day > 31 ||
615 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
616 (month == 2 && year % 400 == 0 && day > 29) ||
617 (month == 2 && year % 100 == 0 && day > 28) ||
618 (month == 2 && year % 4 == 0 && day > 29) ||
619 (month == 2 && year % 4 != 0 && day > 28) ||
620 hour > 23 || minute > 59 || ((
int)floor(second)) > (tableptr->
allowleapsec ? 60 : 59) ||
621 hour < 0 || minute < 0 || ((
int)floor(second)) < 0) {
626 if (year > 0 && year < 100) {
633 ast_str_append(&sql2, 0,
"%s{ts '%04d-%02d-%02d %02d:%02d:%09.6lf'}", separator, year, month, day, hour, minute, second);
640 if (sscanf(colptr,
"%30d", &
integer) != 1) {
654 if ((ret = sscanf(colptr,
"%30lld", &
integer)) != 1) {
667 if (sscanf(colptr,
"%30hd", &
integer) != 1) {
680 if (sscanf(colptr,
"%30hhd", &
integer) != 1) {
693 if (sscanf(colptr,
"%30hhd", &
integer) != 1) {
709 if (sscanf(colptr,
"%30lf", &
number) != 1) {
724 if (sscanf(colptr,
"%30lf", &
number) != 1) {
752 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->
con, &stmt);
753 if (!SQL_SUCCEEDED(res)) {
760 if (!SQL_SUCCEEDED(res)) {
764 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
835 .
requires =
"cel,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()
int ast_cel_backend_unregister(const char *name)
Unregister a CEL backend.
@ AST_CEL_USER_DEFINED
a user-defined event, the event name field should be set
int ast_cel_fill_record(const struct ast_event *event, struct ast_cel_event_record *r)
Fill in an ast_cel_event_record from a CEL event.
#define AST_CEL_EVENT_RECORD_VERSION
struct ABI version
int ast_cel_backend_register(const char *name, ast_cel_backend_cb backend_callback)
Register a CEL backend.
#define LENGTHEN_BUF2(size)
#define CEL_SHOW_USERDEF_DEFAULT
show_user_def is off by default
static int free_config(void)
static void odbc_log(struct ast_event *event)
#define ODBC_BACKEND_NAME
static unsigned char cel_show_user_def
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_INIT(head)
Initializes an rwlist head structure.
#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_RWLIST_HEAD_DESTROY(head)
Destroys an rwlist head structure.
#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.
@ AST_MODULE_LOAD_SUCCESS
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
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.
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a unprepared SQL query.
#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 *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.
Helper struct for getting the fields out of a CEL event.
const char * caller_id_dnid
const char * application_data
const char * account_code
const char * caller_id_rdnis
const char * caller_id_num
const char * channel_name
const char * peer_account
enum ast_cel_event_type event_type
const char * user_defined_name
const char * application_name
struct timeval event_time
uint32_t version
struct ABI version
const char * caller_id_ani
const char * caller_id_name
Structure used to handle boolean flags.
Support for dynamic strings.
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
struct columns::@109 list
unsigned int allowleapsec
struct tables::mysql_columns columns
static struct aco_type item
static struct ast_codec unknown
Time-related functions and macros.