Merge "app_voicemail: Fix memory management issues."
[asterisk/asterisk.git] / cel / cel_odbc.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008 Digium
5  *
6  * Adapted from cdr_adaptive_odbc:
7  * Tilghman Lesher <tlesher AT digium DOT com>
8  * by Steve Murphy
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*! \file
22  *
23  * \brief ODBC CEL backend
24  *
25  * \author Tilghman Lesher \verbatim <tlesher AT digium DOT com> \endverbatim
26  * \ingroup cel_drivers
27  */
28
29 /*** MODULEINFO
30         <depend>res_odbc</depend>
31         <support_level>core</support_level>
32  ***/
33
34 #include "asterisk.h"
35
36 #include <sys/types.h>
37 #include <time.h>
38 #include <math.h>
39
40 #include <sql.h>
41 #include <sqlext.h>
42 #include <sqltypes.h>
43
44 #include "asterisk/config.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/linkedlists.h"
48 #include "asterisk/res_odbc.h"
49 #include "asterisk/cel.h"
50 #include "asterisk/module.h"
51
52 #define CONFIG  "cel_odbc.conf"
53
54 #define ODBC_BACKEND_NAME "ODBC CEL backend"
55
56 /*! \brief show_user_def is off by default */
57 #define CEL_SHOW_USERDEF_DEFAULT        0
58
59 /*! TRUE if we should set the eventtype field to USER_DEFINED on user events. */
60 static unsigned char cel_show_user_def;
61
62 /* Optimization to reduce number of memory allocations */
63 static int maxsize = 512, maxsize2 = 512;
64
65 struct columns {
66         char *name;
67         char *celname;
68         char *filtervalue;
69         char *staticvalue;
70         SQLSMALLINT type;
71         SQLINTEGER size;
72         SQLSMALLINT decimals;
73         SQLSMALLINT radix;
74         SQLSMALLINT nullable;
75         SQLINTEGER octetlen;
76         AST_LIST_ENTRY(columns) list;
77 };
78
79 struct tables {
80         char *connection;
81         char *table;
82         unsigned int usegmtime:1;
83         unsigned int allowleapsec:1;
84         AST_LIST_HEAD_NOLOCK(odbc_columns, columns) columns;
85         AST_RWLIST_ENTRY(tables) list;
86 };
87
88 static AST_RWLIST_HEAD_STATIC(odbc_tables, tables);
89
90 static int load_config(void)
91 {
92         struct ast_config *cfg;
93         struct ast_variable *var;
94         const char *tmp, *catg;
95         struct tables *tableptr;
96         struct columns *entry;
97         struct odbc_obj *obj;
98         char columnname[80];
99         char connection[40];
100         char table[40];
101         int lenconnection, lentable;
102         SQLLEN sqlptr;
103         int res = 0;
104         SQLHSTMT stmt = NULL;
105         struct ast_flags config_flags = { 0 }; /* Part of our config comes from the database */
106
107         cfg = ast_config_load(CONFIG, config_flags);
108         if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
109                 ast_log(LOG_WARNING, "Unable to load " CONFIG ".  No ODBC CEL records!\n");
110                 return -1;
111         }
112
113         /* Process the general category */
114         cel_show_user_def = CEL_SHOW_USERDEF_DEFAULT;
115         for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
116                 if (!strcasecmp(var->name, "show_user_defined")) {
117                         cel_show_user_def = ast_true(var->value) ? 1 : 0;
118                 } else {
119                         /* Unknown option name. */
120                 }
121         }
122
123         for (catg = ast_category_browse(cfg, NULL); catg; catg = ast_category_browse(cfg, catg)) {
124                 if (!strcasecmp(catg, "general")) {
125                         continue;
126                 }
127                 var = ast_variable_browse(cfg, catg);
128                 if (!var)
129                         continue;
130
131                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "connection"))) {
132                         ast_log(LOG_WARNING, "No connection parameter found in '%s'.  Skipping.\n", catg);
133                         continue;
134                 }
135                 ast_copy_string(connection, tmp, sizeof(connection));
136                 lenconnection = strlen(connection);
137
138                 /* When loading, we want to be sure we can connect. */
139                 obj = ast_odbc_request_obj(connection, 1);
140                 if (!obj) {
141                         ast_log(LOG_WARNING, "No such connection '%s' in the '%s' section of " CONFIG ".  Check res_odbc.conf.\n", connection, catg);
142                         continue;
143                 }
144
145                 if (ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "table"))) {
146                         ast_log(LOG_NOTICE, "No table name found.  Assuming 'cel'.\n");
147                         tmp = "cel";
148                 }
149                 ast_copy_string(table, tmp, sizeof(table));
150                 lentable = strlen(table);
151
152                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
153                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
154                         ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", connection);
155                         ast_odbc_release_obj(obj);
156                         continue;
157                 }
158
159                 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)table, SQL_NTS, (unsigned char *)"%", SQL_NTS);
160                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
161                         ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.  Skipping.\n", connection);
162                         ast_odbc_release_obj(obj);
163                         continue;
164                 }
165
166                 tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + lenconnection + 1 + lentable + 1);
167                 if (!tableptr) {
168                         ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", table, connection);
169                         ast_odbc_release_obj(obj);
170                         res = -1;
171                         break;
172                 }
173
174                 tableptr->connection = (char *)tableptr + sizeof(*tableptr);
175                 tableptr->table = (char *)tableptr + sizeof(*tableptr) + lenconnection + 1;
176                 ast_copy_string(tableptr->connection, connection, lenconnection + 1);
177                 ast_copy_string(tableptr->table, table, lentable + 1);
178
179                 tableptr->usegmtime = 0;
180                 if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "usegmtime"))) {
181                         tableptr->usegmtime = ast_true(tmp);
182                 }
183
184                 tableptr->allowleapsec = 1;
185                 if (!ast_strlen_zero(tmp = ast_variable_retrieve(cfg, catg, "allowleapsecond"))) {
186                         tableptr->allowleapsec = ast_true(tmp);
187                 }
188
189                 ast_verb(3, "Found CEL table %s@%s.\n", tableptr->table, tableptr->connection);
190
191                 /* Check for filters first */
192                 for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
193                         if (strncmp(var->name, "filter", 6) == 0) {
194                                 char *celvar = ast_strdupa(var->name + 6);
195                                 celvar = ast_strip(celvar);
196                                 ast_verb(3, "Found filter %s for cel variable %s in %s@%s\n", var->value, celvar, tableptr->table, tableptr->connection);
197
198                                 entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(celvar) + 1 + strlen(var->value) + 1);
199                                 if (!entry) {
200                                         ast_log(LOG_ERROR, "Out of memory creating filter entry for CEL variable '%s' in table '%s' on connection '%s'\n", celvar, table, connection);
201                                         res = -1;
202                                         break;
203                                 }
204
205                                 /* NULL column entry means this isn't a column in the database */
206                                 entry->name = NULL;
207                                 entry->celname = (char *)entry + sizeof(*entry);
208                                 entry->filtervalue = (char *)entry + sizeof(*entry) + strlen(celvar) + 1;
209                                 strcpy(entry->celname, celvar);
210                                 strcpy(entry->filtervalue, var->value);
211
212                                 AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
213                         }
214                 }
215
216                 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
217                         char *celvar = "", *staticvalue = "";
218
219                         SQLGetData(stmt,  4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
220
221                         /* Is there an alias for this column? */
222
223                         /* NOTE: This seems like a non-optimal parse method, but I'm going
224                          * for user configuration readability, rather than fast parsing. We
225                          * really don't parse this file all that often, anyway.
226                          */
227                         for (var = ast_variable_browse(cfg, catg); var; var = var->next) {
228                                 if (strncmp(var->name, "alias", 5) == 0 && strcasecmp(var->value, columnname) == 0) {
229                                         char *alias = ast_strdupa(var->name + 5);
230                                         celvar = ast_strip(alias);
231                                         ast_verb(3, "Found alias %s for column %s in %s@%s\n", celvar, columnname, tableptr->table, tableptr->connection);
232                                         break;
233                                 } else if (strncmp(var->name, "static", 6) == 0 && strcasecmp(var->value, columnname) == 0) {
234                                         char *item = ast_strdupa(var->name + 6);
235                                         item = ast_strip(item);
236                                         if (item[0] == '"' && item[strlen(item) - 1] == '"') {
237                                                 /* Remove surrounding quotes */
238                                                 item[strlen(item) - 1] = '\0';
239                                                 item++;
240                                         }
241                                         staticvalue = item;
242                                 }
243                         }
244
245                         entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1 + strlen(celvar) + 1 + strlen(staticvalue) + 1);
246                         if (!entry) {
247                                 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, table, connection);
248                                 res = -1;
249                                 break;
250                         }
251                         entry->name = (char *)entry + sizeof(*entry);
252                         strcpy(entry->name, columnname);
253
254                         if (!ast_strlen_zero(celvar)) {
255                                 entry->celname = entry->name + strlen(columnname) + 1;
256                                 strcpy(entry->celname, celvar);
257                         } else { /* Point to same place as the column name */
258                                 entry->celname = (char *)entry + sizeof(*entry);
259                         }
260
261                         if (!ast_strlen_zero(staticvalue)) {
262                                 entry->staticvalue = entry->celname + strlen(entry->celname) + 1;
263                                 strcpy(entry->staticvalue, staticvalue);
264                         }
265
266                         SQLGetData(stmt,  5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
267                         SQLGetData(stmt,  7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
268                         SQLGetData(stmt,  9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
269                         SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
270                         SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
271                         SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
272
273                         /* Specification states that the octenlen should be the maximum number of bytes
274                          * returned in a char or binary column, but it seems that some drivers just set
275                          * it to NULL. (Bad Postgres! No biscuit!) */
276                         if (entry->octetlen == 0)
277                                 entry->octetlen = entry->size;
278
279                         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);
280                         /* Insert column info into column list */
281                         AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
282                         res = 0;
283                 }
284
285                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
286                 ast_odbc_release_obj(obj);
287
288                 if (AST_LIST_FIRST(&(tableptr->columns)))
289                         AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
290                 else
291                         ast_free(tableptr);
292         }
293         ast_config_destroy(cfg);
294         return res;
295 }
296
297 static int free_config(void)
298 {
299         struct tables *table;
300         struct columns *entry;
301         while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
302                 while ((entry = AST_LIST_REMOVE_HEAD(&(table->columns), list))) {
303                         ast_free(entry);
304                 }
305                 ast_free(table);
306         }
307         return 0;
308 }
309
310 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
311 {
312         int res, i;
313         char *sql = data;
314         SQLHSTMT stmt;
315         SQLINTEGER nativeerror = 0, numfields = 0;
316         SQLSMALLINT diagbytes = 0;
317         unsigned char state[10], diagnostic[256];
318
319         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
320         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
321                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
322                 return NULL;
323         }
324
325         res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
326         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
327                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
328                 SQLGetDiagField(SQL_HANDLE_STMT, stmt, 1, SQL_DIAG_NUMBER, &numfields, SQL_IS_INTEGER, &diagbytes);
329                 for (i = 0; i < numfields; i++) {
330                         SQLGetDiagRec(SQL_HANDLE_STMT, stmt, i + 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
331                         ast_log(LOG_WARNING, "SQL Execute returned an error %d: %s: %s (%d)\n", res, state, diagnostic, diagbytes);
332                         if (i > 10) {
333                                 ast_log(LOG_WARNING, "Oh, that was good.  There are really %d diagnostics?\n", (int)numfields);
334                                 break;
335                         }
336                 }
337                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
338                 return NULL;
339         }
340
341         return stmt;
342 }
343
344 #define LENGTHEN_BUF(size, var_sql)                                                                                                             \
345                         do {                                                                                                                            \
346                                 /* Lengthen buffer, if necessary */                                                             \
347                                 if (ast_str_strlen(var_sql) + size + 1 > ast_str_size(var_sql)) {               \
348                                         if (ast_str_make_space(&var_sql, ((ast_str_size(var_sql) + size + 1) / 512 + 1) * 512) != 0) { \
349                                                 ast_log(LOG_ERROR, "Unable to allocate sufficient memory.  Insert CEL '%s:%s' failed.\n", tableptr->connection, tableptr->table); \
350                                                 ast_free(sql);                                                                                  \
351                                                 ast_free(sql2);                                                                                 \
352                                                 AST_RWLIST_UNLOCK(&odbc_tables);                                                \
353                                                 return;                                                                                                 \
354                                         }                                                                                                                       \
355                                 }                                                                                                                               \
356                         } while (0)
357
358 #define LENGTHEN_BUF1(size) \
359         LENGTHEN_BUF(size, sql);
360
361 #define LENGTHEN_BUF2(size) \
362         LENGTHEN_BUF(size, sql2);
363
364 static void odbc_log(struct ast_event *event)
365 {
366         struct tables *tableptr;
367         struct columns *entry;
368         struct odbc_obj *obj;
369         struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
370         char *tmp;
371         char colbuf[1024], *colptr;
372         SQLHSTMT stmt = NULL;
373         SQLLEN rows = 0;
374         struct ast_cel_event_record record = {
375                 .version = AST_CEL_EVENT_RECORD_VERSION,
376         };
377
378         if (ast_cel_fill_record(event, &record)) {
379                 return;
380         }
381
382         if (!sql || !sql2) {
383                 if (sql)
384                         ast_free(sql);
385                 if (sql2)
386                         ast_free(sql2);
387                 return;
388         }
389
390         if (AST_RWLIST_RDLOCK(&odbc_tables)) {
391                 ast_log(LOG_ERROR, "Unable to lock table list.  Insert CEL(s) failed.\n");
392                 ast_free(sql);
393                 ast_free(sql2);
394                 return;
395         }
396
397         AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
398                 char *separator = "";
399                 ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
400                 ast_str_set(&sql2, 0, " VALUES (");
401
402                 /* No need to check the connection now; we'll handle any failure in prepare_and_execute */
403                 if (!(obj = ast_odbc_request_obj(tableptr->connection, 0))) {
404                         ast_log(LOG_WARNING, "Unable to retrieve database handle for '%s:%s'.  CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
405                         continue;
406                 }
407
408                 AST_LIST_TRAVERSE(&(tableptr->columns), entry, list) {
409                         int datefield = 0;
410                         int unknown = 0;
411                         if (strcasecmp(entry->celname, "eventtime") == 0) {
412                                 datefield = 1;
413                         }
414
415                         /* Check if we have a similarly named variable */
416                         if (entry->staticvalue) {
417                                 colptr = ast_strdupa(entry->staticvalue);
418                         } else if (datefield) {
419                                 struct timeval date_tv = record.event_time;
420                                 struct ast_tm tm = { 0, };
421                                 ast_localtime(&date_tv, &tm, tableptr->usegmtime ? "UTC" : NULL);
422                                 /* SQL server 2008 added datetime2 and datetimeoffset data types, that
423                                    are reported to SQLColumns() as SQL_WVARCHAR, according to "Enhanced
424                                    Date/Time Type Behavior with Previous SQL Server Versions (ODBC)".
425                                    Here we format the event time with fraction seconds, so these new
426                                    column types will be set to high-precision event time. However, 'date'
427                                    and 'time' columns, also newly introduced, reported as SQL_WVARCHAR
428                                    too, and insertion of the value formatted here into these will fail.
429                                    This should be ok, however, as nobody is going to store just event
430                                    date or just time for CDR purposes.
431                                  */
432                                 ast_strftime(colbuf, sizeof(colbuf), "%Y-%m-%d %H:%M:%S.%6q", &tm);
433                                 colptr = colbuf;
434                         } else {
435                                 if (strcmp(entry->celname, "userdeftype") == 0) {
436                                         ast_copy_string(colbuf, record.user_defined_name, sizeof(colbuf));
437                                 } else if (strcmp(entry->celname, "cid_name") == 0) {
438                                         ast_copy_string(colbuf, record.caller_id_name, sizeof(colbuf));
439                                 } else if (strcmp(entry->celname, "cid_num") == 0) {
440                                         ast_copy_string(colbuf, record.caller_id_num, sizeof(colbuf));
441                                 } else if (strcmp(entry->celname, "cid_ani") == 0) {
442                                         ast_copy_string(colbuf, record.caller_id_ani, sizeof(colbuf));
443                                 } else if (strcmp(entry->celname, "cid_rdnis") == 0) {
444                                         ast_copy_string(colbuf, record.caller_id_rdnis, sizeof(colbuf));
445                                 } else if (strcmp(entry->celname, "cid_dnid") == 0) {
446                                         ast_copy_string(colbuf, record.caller_id_dnid, sizeof(colbuf));
447                                 } else if (strcmp(entry->celname, "exten") == 0) {
448                                         ast_copy_string(colbuf, record.extension, sizeof(colbuf));
449                                 } else if (strcmp(entry->celname, "context") == 0) {
450                                         ast_copy_string(colbuf, record.context, sizeof(colbuf));
451                                 } else if (strcmp(entry->celname, "channame") == 0) {
452                                         ast_copy_string(colbuf, record.channel_name, sizeof(colbuf));
453                                 } else if (strcmp(entry->celname, "appname") == 0) {
454                                         ast_copy_string(colbuf, record.application_name, sizeof(colbuf));
455                                 } else if (strcmp(entry->celname, "appdata") == 0) {
456                                         ast_copy_string(colbuf, record.application_data, sizeof(colbuf));
457                                 } else if (strcmp(entry->celname, "accountcode") == 0) {
458                                         ast_copy_string(colbuf, record.account_code, sizeof(colbuf));
459                                 } else if (strcmp(entry->celname, "peeraccount") == 0) {
460                                         ast_copy_string(colbuf, record.peer_account, sizeof(colbuf));
461                                 } else if (strcmp(entry->celname, "uniqueid") == 0) {
462                                         ast_copy_string(colbuf, record.unique_id, sizeof(colbuf));
463                                 } else if (strcmp(entry->celname, "linkedid") == 0) {
464                                         ast_copy_string(colbuf, record.linked_id, sizeof(colbuf));
465                                 } else if (strcmp(entry->celname, "userfield") == 0) {
466                                         ast_copy_string(colbuf, record.user_field, sizeof(colbuf));
467                                 } else if (strcmp(entry->celname, "peer") == 0) {
468                                         ast_copy_string(colbuf, record.peer, sizeof(colbuf));
469                                 } else if (strcmp(entry->celname, "amaflags") == 0) {
470                                         snprintf(colbuf, sizeof(colbuf), "%u", record.amaflag);
471                                 } else if (strcmp(entry->celname, "extra") == 0) {
472                                         ast_copy_string(colbuf, record.extra, sizeof(colbuf));
473                                 } else if (strcmp(entry->celname, "eventtype") == 0) {
474                                         snprintf(colbuf, sizeof(colbuf), "%u", record.event_type);
475                                 } else {
476                                         colbuf[0] = 0;
477                                         unknown = 1;
478                                 }
479                                 colptr = colbuf;
480                         }
481
482                         if (colptr && !unknown) {
483                                 /* Check first if the column filters this entry.  Note that this
484                                  * is very specifically NOT ast_strlen_zero(), because the filter
485                                  * could legitimately specify that the field is blank, which is
486                                  * different from the field being unspecified (NULL). */
487                                 if (entry->filtervalue && strcasecmp(colptr, entry->filtervalue) != 0) {
488                                         ast_verb(4, "CEL column '%s' with value '%s' does not match filter of"
489                                                 " '%s'.  Cancelling this CEL.\n",
490                                                 entry->celname, colptr, entry->filtervalue);
491                                         goto early_release;
492                                 }
493
494                                 /* Only a filter? */
495                                 if (ast_strlen_zero(entry->name))
496                                         continue;
497
498                                 LENGTHEN_BUF1(strlen(entry->name));
499
500                                 switch (entry->type) {
501                                 case SQL_CHAR:
502                                 case SQL_VARCHAR:
503                                 case SQL_LONGVARCHAR:
504 #ifdef HAVE_ODBC_WCHAR
505                                 case SQL_WCHAR:
506                                 case SQL_WVARCHAR:
507                                 case SQL_WLONGVARCHAR:
508 #endif
509                                 case SQL_BINARY:
510                                 case SQL_VARBINARY:
511                                 case SQL_LONGVARBINARY:
512                                 case SQL_GUID:
513                                         /* For these two field names, get the rendered form, instead of the raw
514                                          * form (but only when we're dealing with a character-based field).
515                                          */
516                                         if (strcasecmp(entry->name, "eventtype") == 0) {
517                                                 const char *event_name;
518
519                                                 event_name = (!cel_show_user_def
520                                                         && record.event_type == AST_CEL_USER_DEFINED)
521                                                         ? record.user_defined_name : record.event_name;
522                                                 snprintf(colbuf, sizeof(colbuf), "%s", event_name);
523                                         }
524
525                                         /* Truncate too-long fields */
526                                         if (entry->type != SQL_GUID) {
527                                                 if (strlen(colptr) > entry->octetlen) {
528                                                         colptr[entry->octetlen] = '\0';
529                                                 }
530                                         }
531
532                                         ast_str_append(&sql, 0, "%s%s", separator, entry->name);
533                                         LENGTHEN_BUF2(strlen(colptr));
534
535                                         /* Encode value, with escaping */
536                                         ast_str_append(&sql2, 0, "%s'", separator);
537                                         for (tmp = colptr; *tmp; tmp++) {
538                                                 if (*tmp == '\'') {
539                                                         ast_str_append(&sql2, 0, "''");
540                                                 } else if (*tmp == '\\' && ast_odbc_backslash_is_escape(obj)) {
541                                                         ast_str_append(&sql2, 0, "\\\\");
542                                                 } else {
543                                                         ast_str_append(&sql2, 0, "%c", *tmp);
544                                                 }
545                                         }
546                                         ast_str_append(&sql2, 0, "'");
547                                         break;
548                                 case SQL_TYPE_DATE:
549                                         if (ast_strlen_zero(colptr)) {
550                                                 continue;
551                                         } else {
552                                                 int year = 0, month = 0, day = 0;
553                                                 if (strcasecmp(entry->name, "eventdate") == 0) {
554                                                         struct ast_tm tm;
555                                                         ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
556                                                         year = tm.tm_year + 1900;
557                                                         month = tm.tm_mon + 1;
558                                                         day = tm.tm_mday;
559                                                 } else {
560                                                         if (sscanf(colptr, "%4d-%2d-%2d", &year, &month, &day) != 3 || year <= 0 ||
561                                                                 month <= 0 || month > 12 || day < 0 || day > 31 ||
562                                                                 ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
563                                                                 (month == 2 && year % 400 == 0 && day > 29) ||
564                                                                 (month == 2 && year % 100 == 0 && day > 28) ||
565                                                                 (month == 2 && year % 4 == 0 && day > 29) ||
566                                                                 (month == 2 && year % 4 != 0 && day > 28)) {
567                                                                 ast_log(LOG_WARNING, "CEL variable %s is not a valid date ('%s').\n", entry->name, colptr);
568                                                                 continue;
569                                                         }
570
571                                                         if (year > 0 && year < 100) {
572                                                                 year += 2000;
573                                                         }
574                                                 }
575
576                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
577                                                 LENGTHEN_BUF2(17);
578                                                 ast_str_append(&sql2, 0, "%s{d '%04d-%02d-%02d'}", separator, year, month, day);
579                                         }
580                                         break;
581                                 case SQL_TYPE_TIME:
582                                         if (ast_strlen_zero(colptr)) {
583                                                 continue;
584                                         } else {
585                                                 int hour = 0, minute = 0, second = 0;
586                                                 if (strcasecmp(entry->name, "eventdate") == 0) {
587                                                         struct ast_tm tm;
588                                                         ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
589                                                         hour = tm.tm_hour;
590                                                         minute = tm.tm_min;
591                                                         second = (tableptr->allowleapsec || tm.tm_sec < 60) ? tm.tm_sec : 59;
592                                                 } else {
593                                                         int count = sscanf(colptr, "%2d:%2d:%2d", &hour, &minute, &second);
594
595                                                         if ((count != 2 && count != 3) || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > (tableptr->allowleapsec ? 60 : 59)) {
596                                                                 ast_log(LOG_WARNING, "CEL variable %s is not a valid time ('%s').\n", entry->name, colptr);
597                                                                 continue;
598                                                         }
599                                                 }
600
601                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
602                                                 LENGTHEN_BUF2(15);
603                                                 ast_str_append(&sql2, 0, "%s{t '%02d:%02d:%02d'}", separator, hour, minute, second);
604                                         }
605                                         break;
606                                 case SQL_TYPE_TIMESTAMP:
607                                 case SQL_TIMESTAMP:
608                                         if (ast_strlen_zero(colptr)) {
609                                                 continue;
610                                         } else {
611                                                 if (datefield) {
612                                                         /*
613                                                          * We've already properly formatted the timestamp so there's no need
614                                                          * to parse it and re-format it.
615                                                          */
616                                                         ast_str_append(&sql, 0, "%s%s", separator, entry->name);
617                                                         LENGTHEN_BUF2(27);
618                                                         ast_str_append(&sql2, 0, "%s{ts '%s'}", separator, colptr);
619                                                 } else {
620                                                         int year = 0, month = 0, day = 0, hour = 0, minute = 0;
621                                                         /* MUST use double for microsecond precision */
622                                                         double second = 0.0;
623                                                         if (strcasecmp(entry->name, "eventdate") == 0) {
624                                                                 /*
625                                                                  * There doesn't seem to be any reference to 'eventdate' anywhere
626                                                                  * other than in this module.  It should be considered for removal
627                                                                  * at a later date.
628                                                                  */
629                                                                 struct ast_tm tm;
630                                                                 ast_localtime(&record.event_time, &tm, tableptr->usegmtime ? "UTC" : NULL);
631                                                                 year = tm.tm_year + 1900;
632                                                                 month = tm.tm_mon + 1;
633                                                                 day = tm.tm_mday;
634                                                                 hour = tm.tm_hour;
635                                                                 minute = tm.tm_min;
636                                                                 second = (tableptr->allowleapsec || tm.tm_sec < 60) ? tm.tm_sec : 59;
637                                                                 second += (tm.tm_usec / 1000000.0);
638                                                         } else {
639                                                                 /*
640                                                                  * If we're here, the data to be inserted MAY be a timestamp
641                                                                  * but the column is.  We parse as much as we can.
642                                                                  */
643                                                                 int count = sscanf(colptr, "%4d-%2d-%2d %2d:%2d:%lf", &year, &month, &day, &hour, &minute, &second);
644
645                                                                 if ((count != 3 && count != 5 && count != 6) || year <= 0 ||
646                                                                         month <= 0 || month > 12 || day < 0 || day > 31 ||
647                                                                         ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) ||
648                                                                         (month == 2 && year % 400 == 0 && day > 29) ||
649                                                                         (month == 2 && year % 100 == 0 && day > 28) ||
650                                                                         (month == 2 && year % 4 == 0 && day > 29) ||
651                                                                         (month == 2 && year % 4 != 0 && day > 28) ||
652                                                                         hour > 23 || minute > 59 || ((int)floor(second)) > (tableptr->allowleapsec ? 60 : 59) ||
653                                                                         hour < 0 || minute < 0 || ((int)floor(second)) < 0) {
654                                                                         ast_log(LOG_WARNING, "CEL variable %s is not a valid timestamp ('%s').\n", entry->name, colptr);
655                                                                         continue;
656                                                                 }
657
658                                                                 if (year > 0 && year < 100) {
659                                                                         year += 2000;
660                                                                 }
661                                                         }
662
663                                                         ast_str_append(&sql, 0, "%s%s", separator, entry->name);
664                                                         LENGTHEN_BUF2(27);
665                                                         ast_str_append(&sql2, 0, "%s{ts '%04d-%02d-%02d %02d:%02d:%09.6lf'}", separator, year, month, day, hour, minute, second);
666                                                 }
667                                         }
668                                         break;
669                                 case SQL_INTEGER:
670                                         {
671                                                 int integer = 0;
672                                                 if (sscanf(colptr, "%30d", &integer) != 1) {
673                                                         ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
674                                                         continue;
675                                                 }
676
677                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
678                                                 LENGTHEN_BUF2(12);
679                                                 ast_str_append(&sql2, 0, "%s%d", separator, integer);
680                                         }
681                                         break;
682                                 case SQL_BIGINT:
683                                         {
684                                                 long long integer = 0;
685                                                 int ret;
686                                                 if ((ret = sscanf(colptr, "%30lld", &integer)) != 1) {
687                                                         ast_log(LOG_WARNING, "CEL variable %s is not an integer. (%d - '%s')\n", entry->name, ret, colptr);
688                                                         continue;
689                                                 }
690
691                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
692                                                 LENGTHEN_BUF2(24);
693                                                 ast_str_append(&sql2, 0, "%s%lld", separator, integer);
694                                         }
695                                         break;
696                                 case SQL_SMALLINT:
697                                         {
698                                                 short integer = 0;
699                                                 if (sscanf(colptr, "%30hd", &integer) != 1) {
700                                                         ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
701                                                         continue;
702                                                 }
703
704                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
705                                                 LENGTHEN_BUF2(7);
706                                                 ast_str_append(&sql2, 0, "%s%d", separator, integer);
707                                         }
708                                         break;
709                                 case SQL_TINYINT:
710                                         {
711                                                 signed char integer = 0;
712                                                 if (sscanf(colptr, "%30hhd", &integer) != 1) {
713                                                         ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
714                                                         continue;
715                                                 }
716
717                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
718                                                 LENGTHEN_BUF2(4);
719                                                 ast_str_append(&sql2, 0, "%s%d", separator, integer);
720                                         }
721                                         break;
722                                 case SQL_BIT:
723                                         {
724                                                 signed char integer = 0;
725                                                 if (sscanf(colptr, "%30hhd", &integer) != 1) {
726                                                         ast_log(LOG_WARNING, "CEL variable %s is not an integer.\n", entry->name);
727                                                         continue;
728                                                 }
729                                                 if (integer != 0)
730                                                         integer = 1;
731
732                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
733                                                 LENGTHEN_BUF2(2);
734                                                 ast_str_append(&sql2, 0, "%s%d", separator, integer);
735                                         }
736                                         break;
737                                 case SQL_NUMERIC:
738                                 case SQL_DECIMAL:
739                                         {
740                                                 double number = 0.0;
741                                                 if (sscanf(colptr, "%30lf", &number) != 1) {
742                                                         ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);
743                                                         continue;
744                                                 }
745
746                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
747                                                 LENGTHEN_BUF2(entry->decimals + 2);
748                                                 ast_str_append(&sql2, 0, "%s%*.*lf", separator, entry->decimals, entry->radix, number);
749                                         }
750                                         break;
751                                 case SQL_FLOAT:
752                                 case SQL_REAL:
753                                 case SQL_DOUBLE:
754                                         {
755                                                 double number = 0.0;
756                                                 if (sscanf(colptr, "%30lf", &number) != 1) {
757                                                         ast_log(LOG_WARNING, "CEL variable %s is not an numeric type.\n", entry->name);
758                                                         continue;
759                                                 }
760
761                                                 ast_str_append(&sql, 0, "%s%s", separator, entry->name);
762                                                 LENGTHEN_BUF2(entry->decimals);
763                                                 ast_str_append(&sql2, 0, "%s%lf", separator, number);
764                                         }
765                                         break;
766                                 default:
767                                         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);
768                                         continue;
769                                 }
770                                 separator = ", ";
771                         }
772                 }
773
774                 /* Concatenate the two constructed buffers */
775                 LENGTHEN_BUF1(ast_str_strlen(sql2));
776                 ast_str_append(&sql, 0, ")");
777                 ast_str_append(&sql2, 0, ")");
778                 ast_str_append(&sql, 0, "%s", ast_str_buffer(sql2));
779
780                 ast_debug(3, "Executing SQL statement: [%s]\n", ast_str_buffer(sql));
781                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, ast_str_buffer(sql));
782                 if (stmt) {
783                         SQLRowCount(stmt, &rows);
784                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
785                 }
786                 if (rows == 0) {
787                         ast_log(LOG_WARNING, "Insert failed on '%s:%s'.  CEL failed: %s\n", tableptr->connection, tableptr->table, ast_str_buffer(sql));
788                 }
789 early_release:
790                 ast_odbc_release_obj(obj);
791         }
792         AST_RWLIST_UNLOCK(&odbc_tables);
793
794         /* Next time, just allocate buffers that are that big to start with. */
795         if (ast_str_strlen(sql) > maxsize) {
796                 maxsize = ast_str_strlen(sql);
797         }
798         if (ast_str_strlen(sql2) > maxsize2) {
799                 maxsize2 = ast_str_strlen(sql2);
800         }
801
802         ast_free(sql);
803         ast_free(sql2);
804 }
805
806 static int unload_module(void)
807 {
808         if (AST_RWLIST_WRLOCK(&odbc_tables)) {
809                 ast_log(LOG_ERROR, "Unable to lock column list.  Unload failed.\n");
810                 return -1;
811         }
812
813         ast_cel_backend_unregister(ODBC_BACKEND_NAME);
814         free_config();
815         AST_RWLIST_UNLOCK(&odbc_tables);
816         AST_RWLIST_HEAD_DESTROY(&odbc_tables);
817         
818         return 0;
819 }
820
821 static int load_module(void)
822 {
823         AST_RWLIST_HEAD_INIT(&odbc_tables);
824
825         if (AST_RWLIST_WRLOCK(&odbc_tables)) {
826                 ast_log(LOG_ERROR, "Unable to lock column list.  Load failed.\n");
827                 return AST_MODULE_LOAD_DECLINE;
828         }
829         load_config();
830         AST_RWLIST_UNLOCK(&odbc_tables);
831         if (ast_cel_backend_register(ODBC_BACKEND_NAME, odbc_log)) {
832                 ast_log(LOG_ERROR, "Unable to subscribe to CEL events\n");
833                 free_config();
834                 return AST_MODULE_LOAD_DECLINE;
835         }
836         return AST_MODULE_LOAD_SUCCESS;
837 }
838
839 static int reload(void)
840 {
841         if (AST_RWLIST_WRLOCK(&odbc_tables)) {
842                 ast_log(LOG_ERROR, "Unable to lock column list.  Reload failed.\n");
843                 return AST_MODULE_LOAD_DECLINE;
844         }
845
846         free_config();
847         load_config();
848         AST_RWLIST_UNLOCK(&odbc_tables);
849         return AST_MODULE_LOAD_SUCCESS;
850 }
851
852 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, ODBC_BACKEND_NAME,
853         .support_level = AST_MODULE_SUPPORT_CORE,
854         .load = load_module,
855         .unload = unload_module,
856         .reload = reload,
857         .load_pri = AST_MODPRI_CDR_DRIVER,
858 );
859