Merge the adaptive realtime branch, which will make adding new required fields
[asterisk/asterisk.git] / res / res_config_odbc.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Copyright (C) 2004 - 2005 Anthony Minessale II <anthmct@yahoo.com>
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+odbc plugin for portable configuration engine
24  *
25  * \author Mark Spencer <markster@digium.com>
26  * \author Anthony Minessale II <anthmct@yahoo.com>
27  *
28  * \arg http://www.unixodbc.org
29  */
30
31 /*** MODULEINFO
32         <depend>unixodbc</depend>
33         <depend>ltdl</depend>
34         <depend>res_odbc</depend>
35  ***/
36
37 #include "asterisk.h"
38
39 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40
41 #include "asterisk/file.h"
42 #include "asterisk/channel.h"
43 #include "asterisk/pbx.h"
44 #include "asterisk/config.h"
45 #include "asterisk/module.h"
46 #include "asterisk/lock.h"
47 #include "asterisk/res_odbc.h"
48 #include "asterisk/utils.h"
49
50 struct custom_prepare_struct {
51         const char *sql;
52         const char *extra;
53         va_list ap;
54         unsigned long long skip;
55 };
56
57 /*!\brief The structures referenced are in include/asterisk/res_odbc.h */
58 static AST_RWLIST_HEAD_STATIC(odbc_tables, odbc_cache_tables);
59
60 static void destroy_table_cache(struct odbc_cache_tables *table) {
61         struct odbc_cache_columns *col;
62         ast_debug(1, "Destroying table cache for %s\n", table->table);
63         AST_RWLIST_WRLOCK(&table->columns);
64         while ((col = AST_RWLIST_REMOVE_HEAD(&table->columns, list))) {
65                 ast_free(col);
66         }
67         AST_RWLIST_UNLOCK(&table->columns);
68         AST_RWLIST_HEAD_DESTROY(&table->columns);
69         ast_free(table);
70 }
71
72 #define release_table(ptr) if (ptr) { AST_RWLIST_UNLOCK(&(ptr)->columns); }
73
74 /*!
75  * \brief Find or create an entry describing the table specified.
76  * \param obj An active ODBC handle on which to query the table
77  * \param table Tablename to describe
78  * \retval A structure describing the table layout, or NULL, if the table is not found or another error occurs.
79  * When a structure is returned, the contained columns list will be
80  * rdlock'ed, to ensure that it will be retained in memory.
81  */
82 static struct odbc_cache_tables *find_table(const char *database, const char *tablename)
83 {
84         struct odbc_cache_tables *tableptr;
85         struct odbc_cache_columns *entry;
86         char columnname[80];
87         SQLLEN sqlptr;
88         SQLHSTMT stmt = NULL;
89         int res = 0, error = 0, try = 0;
90         struct odbc_obj *obj = ast_odbc_request_obj(database, 0);
91
92         AST_RWLIST_RDLOCK(&odbc_tables);
93         AST_RWLIST_TRAVERSE(&odbc_tables, tableptr, list) {
94                 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
95                         break;
96                 }
97         }
98         if (tableptr) {
99                 AST_RWLIST_RDLOCK(&tableptr->columns);
100                 AST_RWLIST_UNLOCK(&odbc_tables);
101                 return tableptr;
102         }
103
104         if (!obj) {
105                 ast_log(LOG_WARNING, "Unable to retrieve database handle for table description '%s@%s'\n", tablename, database);
106                 return NULL;
107         }
108
109         /* Table structure not already cached; build it now. */
110         do {
111 retry:
112                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
113                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
114                         if (try == 0) {
115                                 try = 1;
116                                 ast_odbc_sanity_check(obj);
117                                 goto retry;
118                         }
119                         ast_log(LOG_WARNING, "SQL Alloc Handle failed on connection '%s'!\n", database);
120                         break;
121                 }
122
123                 res = SQLColumns(stmt, NULL, 0, NULL, 0, (unsigned char *)tablename, SQL_NTS, (unsigned char *)"%", SQL_NTS);
124                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
125                         if (try == 0) {
126                                 try = 1;
127                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
128                                 ast_odbc_sanity_check(obj);
129                                 goto retry;
130                         }
131                         ast_log(LOG_ERROR, "Unable to query database columns on connection '%s'.\n", database);
132                         break;
133                 }
134
135                 if (!(tableptr = ast_calloc(sizeof(char), sizeof(*tableptr) + strlen(database) + 1 + strlen(tablename) + 1))) {
136                         ast_log(LOG_ERROR, "Out of memory creating entry for table '%s' on connection '%s'\n", tablename, database);
137                         break;
138                 }
139
140                 tableptr->connection = (char *)tableptr + sizeof(*tableptr);
141                 tableptr->table = (char *)tableptr + sizeof(*tableptr) + strlen(database) + 1;
142                 strcpy(tableptr->connection, database); /* SAFE */
143                 strcpy(tableptr->table, tablename); /* SAFE */
144                 AST_RWLIST_HEAD_INIT(&(tableptr->columns));
145
146                 while ((res = SQLFetch(stmt)) != SQL_NO_DATA && res != SQL_ERROR) {
147                         SQLGetData(stmt,  4, SQL_C_CHAR, columnname, sizeof(columnname), &sqlptr);
148
149                         if (!(entry = ast_calloc(sizeof(char), sizeof(*entry) + strlen(columnname) + 1))) {
150                                 ast_log(LOG_ERROR, "Out of memory creating entry for column '%s' in table '%s' on connection '%s'\n", columnname, tablename, database);
151                                 error = 1;
152                                 break;
153                         }
154                         entry->name = (char *)entry + sizeof(*entry);
155                         strcpy(entry->name, columnname);
156
157                         SQLGetData(stmt,  5, SQL_C_SHORT, &entry->type, sizeof(entry->type), NULL);
158                         SQLGetData(stmt,  7, SQL_C_LONG, &entry->size, sizeof(entry->size), NULL);
159                         SQLGetData(stmt,  9, SQL_C_SHORT, &entry->decimals, sizeof(entry->decimals), NULL);
160                         SQLGetData(stmt, 10, SQL_C_SHORT, &entry->radix, sizeof(entry->radix), NULL);
161                         SQLGetData(stmt, 11, SQL_C_SHORT, &entry->nullable, sizeof(entry->nullable), NULL);
162                         SQLGetData(stmt, 16, SQL_C_LONG, &entry->octetlen, sizeof(entry->octetlen), NULL);
163
164                         /* Specification states that the octenlen should be the maximum number of bytes
165                          * returned in a char or binary column, but it seems that some drivers just set
166                          * it to NULL. (Bad Postgres! No biscuit!) */
167                         if (entry->octetlen == 0) {
168                                 entry->octetlen = entry->size;
169                         }
170
171                         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);
172                         /* Insert column info into column list */
173                         AST_LIST_INSERT_TAIL(&(tableptr->columns), entry, list);
174                 }
175                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
176
177                 AST_RWLIST_INSERT_TAIL(&odbc_tables, tableptr, list);
178                 AST_RWLIST_RDLOCK(&(tableptr->columns));
179         } while (0);
180
181         AST_RWLIST_UNLOCK(&odbc_tables);
182
183         if (error) {
184                 destroy_table_cache(tableptr);
185                 tableptr = NULL;
186         }
187         if (obj) {
188                 ast_odbc_release_obj(obj);
189         }
190         return tableptr;
191 }
192
193 static struct odbc_cache_columns *find_column(struct odbc_cache_tables *table, const char *colname)
194 {
195         struct odbc_cache_columns *col;
196         AST_RWLIST_TRAVERSE(&table->columns, col, list) {
197                 if (strcasecmp(col->name, colname) == 0) {
198                         return col;
199                 }
200         }
201         return NULL;
202 }
203
204 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
205 {
206         int res, x = 1, count = 0;
207         struct custom_prepare_struct *cps = data;
208         const char *newparam, *newval;
209         SQLHSTMT stmt;
210         va_list ap;
211
212         va_copy(ap, cps->ap);
213
214         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
215         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
216                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
217                 return NULL;
218         }
219
220         res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
221         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
222                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
223                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
224                 return NULL;
225         }
226
227         while ((newparam = va_arg(ap, const char *))) {
228                 newval = va_arg(ap, const char *);
229                 if ((1 << count) & cps->skip) {
230                         continue;
231                 }
232                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
233         }
234         va_end(ap);
235
236         if (!ast_strlen_zero(cps->extra))
237                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
238         return stmt;
239 }
240
241 /*!
242  * \brief Excute an SQL query and return ast_variable list
243  * \param database
244  * \param table
245  * \param ap list containing one or more field/operator/value set.
246  *
247  * Select database and preform query on table, prepare the sql statement
248  * Sub-in the values to the prepared statement and execute it. Return results
249  * as a ast_variable list.
250  *
251  * \retval var on success
252  * \retval NULL on failure
253 */
254 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
255 {
256         struct odbc_obj *obj;
257         SQLHSTMT stmt;
258         char sql[1024];
259         char coltitle[256];
260         char rowdata[2048];
261         char *op;
262         const char *newparam, *newval;
263         char *stringp;
264         char *chunk;
265         SQLSMALLINT collen;
266         int res;
267         int x;
268         struct ast_variable *var=NULL, *prev=NULL;
269         SQLULEN colsize;
270         SQLSMALLINT colcount=0;
271         SQLSMALLINT datatype;
272         SQLSMALLINT decimaldigits;
273         SQLSMALLINT nullable;
274         SQLLEN indicator;
275         va_list aq;
276         struct custom_prepare_struct cps = { .sql = sql };
277
278         va_copy(cps.ap, ap);
279         va_copy(aq, ap);
280
281         if (!table)
282                 return NULL;
283
284         obj = ast_odbc_request_obj(database, 0);
285
286         if (!obj) {
287                 ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
288                 return NULL;
289         }
290
291         newparam = va_arg(aq, const char *);
292         if (!newparam)
293                 return NULL;
294         newval = va_arg(aq, const char *);
295         op = !strchr(newparam, ' ') ? " =" : "";
296         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
297                 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
298         while((newparam = va_arg(aq, const char *))) {
299                 op = !strchr(newparam, ' ') ? " =" : "";
300                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
301                         strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
302                 newval = va_arg(aq, const char *);
303         }
304         va_end(aq);
305
306         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
307
308         if (!stmt) {
309                 ast_odbc_release_obj(obj);
310                 return NULL;
311         }
312
313         res = SQLNumResultCols(stmt, &colcount);
314         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
315                 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
316                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
317                 ast_odbc_release_obj(obj);
318                 return NULL;
319         }
320
321         res = SQLFetch(stmt);
322         if (res == SQL_NO_DATA) {
323                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
324                 ast_odbc_release_obj(obj);
325                 return NULL;
326         }
327         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
328                 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
329                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
330                 ast_odbc_release_obj(obj);
331                 return NULL;
332         }
333         for (x = 0; x < colcount; x++) {
334                 rowdata[0] = '\0';
335                 collen = sizeof(coltitle);
336                 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
337                                         &datatype, &colsize, &decimaldigits, &nullable);
338                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
339                         ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
340                         if (var)
341                                 ast_variables_destroy(var);
342                         ast_odbc_release_obj(obj);
343                         return NULL;
344                 }
345
346                 indicator = 0;
347                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
348                 if (indicator == SQL_NULL_DATA)
349                         rowdata[0] = '\0';
350                 else if (ast_strlen_zero(rowdata)) {
351                         /* Because we encode the empty string for a NULL, we will encode
352                          * actual empty strings as a string containing a single whitespace. */
353                         ast_copy_string(rowdata, " ", sizeof(rowdata));
354                 }
355
356                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
357                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
358                         if (var)
359                                 ast_variables_destroy(var);
360                         ast_odbc_release_obj(obj);
361                         return NULL;
362                 }
363                 stringp = rowdata;
364                 while(stringp) {
365                         chunk = strsep(&stringp, ";");
366                         if (!ast_strlen_zero(ast_strip(chunk))) {
367                                 if (prev) {
368                                         prev->next = ast_variable_new(coltitle, chunk, "");
369                                         if (prev->next)
370                                                 prev = prev->next;
371                                 } else 
372                                         prev = var = ast_variable_new(coltitle, chunk, "");
373                         }
374                 }
375         }
376
377
378         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
379         ast_odbc_release_obj(obj);
380         return var;
381 }
382
383 /*!
384  * \brief Excute an Select query and return ast_config list
385  * \param database
386  * \param table
387  * \param ap list containing one or more field/operator/value set.
388  *
389  * Select database and preform query on table, prepare the sql statement
390  * Sub-in the values to the prepared statement and execute it. 
391  * Execute this prepared query against several ODBC connected databases.
392  * Return results as an ast_config variable.
393  *
394  * \retval var on success
395  * \retval NULL on failure
396 */
397 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
398 {
399         struct odbc_obj *obj;
400         SQLHSTMT stmt;
401         char sql[1024];
402         char coltitle[256];
403         char rowdata[2048];
404         const char *initfield=NULL;
405         char *op;
406         const char *newparam, *newval;
407         char *stringp;
408         char *chunk;
409         SQLSMALLINT collen;
410         int res;
411         int x;
412         struct ast_variable *var=NULL;
413         struct ast_config *cfg=NULL;
414         struct ast_category *cat=NULL;
415         SQLULEN colsize;
416         SQLSMALLINT colcount=0;
417         SQLSMALLINT datatype;
418         SQLSMALLINT decimaldigits;
419         SQLSMALLINT nullable;
420         SQLLEN indicator;
421         struct custom_prepare_struct cps = { .sql = sql };
422         va_list aq;
423
424         va_copy(cps.ap, ap);
425         va_copy(aq, ap);
426
427         if (!table)
428                 return NULL;
429
430         obj = ast_odbc_request_obj(database, 0);
431         if (!obj)
432                 return NULL;
433
434         newparam = va_arg(aq, const char *);
435         if (!newparam)  {
436                 ast_odbc_release_obj(obj);
437                 return NULL;
438         }
439         initfield = ast_strdupa(newparam);
440         if ((op = strchr(initfield, ' '))) 
441                 *op = '\0';
442         newval = va_arg(aq, const char *);
443         op = !strchr(newparam, ' ') ? " =" : "";
444         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
445                 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
446         while((newparam = va_arg(aq, const char *))) {
447                 op = !strchr(newparam, ' ') ? " =" : "";
448                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
449                         strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
450                 newval = va_arg(aq, const char *);
451         }
452         if (initfield)
453                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
454         va_end(aq);
455
456         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
457
458         if (!stmt) {
459                 ast_odbc_release_obj(obj);
460                 return NULL;
461         }
462
463         res = SQLNumResultCols(stmt, &colcount);
464         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
465                 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
466                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
467                 ast_odbc_release_obj(obj);
468                 return NULL;
469         }
470
471         cfg = ast_config_new();
472         if (!cfg) {
473                 ast_log(LOG_WARNING, "Out of memory!\n");
474                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
475                 ast_odbc_release_obj(obj);
476                 return NULL;
477         }
478
479         while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
480                 var = NULL;
481                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
482                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
483                         continue;
484                 }
485                 cat = ast_category_new("","",99999);
486                 if (!cat) {
487                         ast_log(LOG_WARNING, "Out of memory!\n");
488                         continue;
489                 }
490                 for (x=0;x<colcount;x++) {
491                         rowdata[0] = '\0';
492                         collen = sizeof(coltitle);
493                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
494                                                 &datatype, &colsize, &decimaldigits, &nullable);
495                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
496                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
497                                 ast_category_destroy(cat);
498                                 continue;
499                         }
500
501                         indicator = 0;
502                         res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
503                         if (indicator == SQL_NULL_DATA)
504                                 continue;
505
506                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
507                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
508                                 ast_category_destroy(cat);
509                                 continue;
510                         }
511                         stringp = rowdata;
512                         while(stringp) {
513                                 chunk = strsep(&stringp, ";");
514                                 if (!ast_strlen_zero(ast_strip(chunk))) {
515                                         if (initfield && !strcmp(initfield, coltitle))
516                                                 ast_category_rename(cat, chunk);
517                                         var = ast_variable_new(coltitle, chunk, "");
518                                         ast_variable_append(cat, var);
519                                 }
520                         }
521                 }
522                 ast_category_append(cfg, cat);
523         }
524
525         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
526         ast_odbc_release_obj(obj);
527         return cfg;
528 }
529
530 /*!
531  * \brief Excute an UPDATE query
532  * \param database
533  * \param table
534  * \param keyfield where clause field
535  * \param lookup value of field for where clause
536  * \param ap list containing one or more field/value set(s).
537  *
538  * Update a database table, prepare the sql statement using keyfield and lookup
539  * control the number of records to change. All values to be changed are stored in ap list.
540  * Sub-in the values to the prepared statement and execute it.
541  *
542  * \retval number of rows affected
543  * \retval -1 on failure
544 */
545 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
546 {
547         struct odbc_obj *obj;
548         SQLHSTMT stmt;
549         char sql[256];
550         SQLLEN rowcount=0;
551         const char *newparam, *newval;
552         int res, count = 0;
553         va_list aq;
554         struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
555         struct odbc_cache_tables *tableptr = find_table(database, table);
556         struct odbc_cache_columns *column;
557
558         va_copy(cps.ap, ap);
559         va_copy(aq, ap);
560         
561         if (!table) {
562                 release_table(tableptr);
563                 return -1;
564         }
565
566         obj = ast_odbc_request_obj(database, 0);
567         if (!obj) {
568                 release_table(tableptr);
569                 return -1;
570         }
571
572         newparam = va_arg(aq, const char *);
573         if (!newparam)  {
574                 ast_odbc_release_obj(obj);
575                 release_table(tableptr);
576                 return -1;
577         }
578         newval = va_arg(aq, const char *);
579
580         if (tableptr && !(column = find_column(tableptr, newparam))) {
581                 ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", newparam, table, database);
582         }
583
584         snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
585         while((newparam = va_arg(aq, const char *))) {
586                 if ((tableptr && (column = find_column(tableptr, newparam))) || count > 63) {
587                         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
588                         newval = va_arg(aq, const char *);
589                 } else { /* the column does not exist in the table OR we've exceeded the space in our flag field */
590                         cps.skip |= (((long long)1) << count);
591                 }
592                 count++;
593         }
594         va_end(aq);
595         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
596         release_table(tableptr);
597
598         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
599
600         if (!stmt) {
601                 ast_odbc_release_obj(obj);
602                 return -1;
603         }
604
605         res = SQLRowCount(stmt, &rowcount);
606         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
607         ast_odbc_release_obj(obj);
608
609         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
610                 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
611                 return -1;
612         }
613
614         if (rowcount >= 0)
615                 return (int)rowcount;
616
617         return -1;
618 }
619
620 /*!
621  * \brief Excute an INSERT query
622  * \param database
623  * \param table
624  * \param ap list containing one or more field/value set(s)
625  *
626  * Insert a new record into database table, prepare the sql statement.
627  * All values to be changed are stored in ap list.
628  * Sub-in the values to the prepared statement and execute it.
629  *
630  * \retval number of rows affected
631  * \retval -1 on failure
632 */
633 static int store_odbc(const char *database, const char *table, va_list ap)
634 {
635         struct odbc_obj *obj;
636         SQLHSTMT stmt;
637         char sql[256];
638         char keys[256];
639         char vals[256];
640         SQLLEN rowcount=0;
641         const char *newparam, *newval;
642         int res;
643         va_list aq;
644         struct custom_prepare_struct cps = { .sql = sql, .extra = NULL };
645
646         va_copy(cps.ap, ap);
647         va_copy(aq, ap);
648         
649         if (!table)
650                 return -1;
651
652         obj = ast_odbc_request_obj(database, 0);
653         if (!obj)
654                 return -1;
655
656         newparam = va_arg(aq, const char *);
657         if (!newparam)  {
658                 ast_odbc_release_obj(obj);
659                 return -1;
660         }
661         newval = va_arg(aq, const char *);
662         snprintf(keys, sizeof(keys), "%s", newparam);
663         ast_copy_string(vals, "?", sizeof(vals));
664         while ((newparam = va_arg(aq, const char *))) {
665                 snprintf(keys + strlen(keys), sizeof(keys) - strlen(keys), ", %s", newparam);
666                 snprintf(vals + strlen(vals), sizeof(vals) - strlen(vals), ", ?");
667                 newval = va_arg(aq, const char *);
668         }
669         va_end(aq);
670         snprintf(sql, sizeof(sql), "INSERT INTO %s (%s) VALUES (%s)", table, keys, vals);
671
672         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
673
674         if (!stmt) {
675                 ast_odbc_release_obj(obj);
676                 return -1;
677         }
678
679         res = SQLRowCount(stmt, &rowcount);
680         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
681         ast_odbc_release_obj(obj);
682
683         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
684                 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
685                 return -1;
686         }
687
688         if (rowcount >= 0)
689                 return (int)rowcount;
690
691         return -1;
692 }
693
694 /*!
695  * \brief Excute an DELETE query
696  * \param database
697  * \param table
698  * \param keyfield where clause field
699  * \param lookup value of field for where clause
700  * \param ap list containing one or more field/value set(s)
701  *
702  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
703  * control the number of records to change. Additional params to match rows are stored in ap list.
704  * Sub-in the values to the prepared statement and execute it.
705  *
706  * \retval number of rows affected
707  * \retval -1 on failure
708 */
709 static int destroy_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
710 {
711         struct odbc_obj *obj;
712         SQLHSTMT stmt;
713         char sql[256];
714         SQLLEN rowcount=0;
715         const char *newparam, *newval;
716         int res;
717         va_list aq;
718         struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
719
720         va_copy(cps.ap, ap);
721         va_copy(aq, ap);
722         
723         if (!table)
724                 return -1;
725
726         obj = ast_odbc_request_obj(database, 0);
727         if (!obj)
728                 return -1;
729
730         snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE ", table);
731         while((newparam = va_arg(aq, const char *))) {
732                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=? AND ", newparam);
733                 newval = va_arg(aq, const char *);
734         }
735         va_end(aq);
736         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s=?", keyfield);
737
738         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
739
740         if (!stmt) {
741                 ast_odbc_release_obj(obj);
742                 return -1;
743         }
744
745         res = SQLRowCount(stmt, &rowcount);
746         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
747         ast_odbc_release_obj(obj);
748
749         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
750                 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
751                 return -1;
752         }
753
754         if (rowcount >= 0)
755                 return (int)rowcount;
756
757         return -1;
758 }
759
760
761 struct config_odbc_obj {
762         char *sql;
763         unsigned long cat_metric;
764         char category[128];
765         char var_name[128];
766         char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */
767         SQLLEN err;
768 };
769
770 static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
771 {
772         struct config_odbc_obj *q = data;
773         SQLHSTMT sth;
774         int res;
775
776         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth);
777         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
778                 ast_verb(4, "Failure in AllocStatement %d\n", res);
779                 return NULL;
780         }
781
782         res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS);
783         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
784                 ast_verb(4, "Error in PREPARE %d\n", res);
785                 SQLFreeHandle(SQL_HANDLE_STMT, sth);
786                 return NULL;
787         }
788
789         SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err);
790         SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err);
791         SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err);
792         SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err);
793
794         return sth;
795 }
796
797 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl, const char *who_asked)
798 {
799         struct ast_variable *new_v;
800         struct ast_category *cur_cat;
801         int res = 0;
802         struct odbc_obj *obj;
803         char sqlbuf[1024] = "";
804         char *sql = sqlbuf;
805         size_t sqlleft = sizeof(sqlbuf);
806         unsigned int last_cat_metric = 0;
807         SQLSMALLINT rowcount = 0;
808         SQLHSTMT stmt;
809         char last[128] = "";
810         struct config_odbc_obj q;
811         struct ast_flags loader_flags = { 0 };
812
813         memset(&q, 0, sizeof(q));
814
815         if (!file || !strcmp (file, "res_config_odbc.conf"))
816                 return NULL;            /* cant configure myself with myself ! */
817
818         obj = ast_odbc_request_obj(database, 0);
819         if (!obj)
820                 return NULL;
821
822         ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table);
823         ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file);
824         ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ");
825         q.sql = sqlbuf;
826
827         stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q);
828
829         if (!stmt) {
830                 ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
831                 ast_odbc_release_obj(obj);
832                 return NULL;
833         }
834
835         res = SQLNumResultCols(stmt, &rowcount);
836
837         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
838                 ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
839                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
840                 ast_odbc_release_obj(obj);
841                 return NULL;
842         }
843
844         if (!rowcount) {
845                 ast_log(LOG_NOTICE, "found nothing\n");
846                 ast_odbc_release_obj(obj);
847                 return cfg;
848         }
849
850         cur_cat = ast_config_get_current_category(cfg);
851
852         while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
853                 if (!strcmp (q.var_name, "#include")) {
854                         if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) {
855                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
856                                 ast_odbc_release_obj(obj);
857                                 return NULL;
858                         }
859                         continue;
860                 } 
861                 if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
862                         cur_cat = ast_category_new(q.category, "", 99999);
863                         if (!cur_cat) {
864                                 ast_log(LOG_WARNING, "Out of memory!\n");
865                                 break;
866                         }
867                         strcpy(last, q.category);
868                         last_cat_metric = q.cat_metric;
869                         ast_category_append(cfg, cur_cat);
870                 }
871
872                 new_v = ast_variable_new(q.var_name, q.var_val, "");
873                 ast_variable_append(cur_cat, new_v);
874         }
875
876         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
877         ast_odbc_release_obj(obj);
878         return cfg;
879 }
880
881 #define warn_length(col, size)  ast_log(LOG_WARNING, "Column %s is not long enough to contain realtime data (needs %d)\n", col->name, size)
882 #define warn_type(col, type)    ast_log(LOG_WARNING, "Column %s is of the incorrect type to contain realtime data\n", col->name)
883
884 static int require_odbc(const char *database, const char *table, va_list ap)
885 {
886         struct odbc_cache_tables *tableptr = find_table(database, table);
887         struct odbc_cache_columns *col;
888         char *elm;
889         int type, size;
890
891         if (!tableptr) {
892                 return -1;
893         }
894
895         while ((elm = va_arg(ap, char *))) {
896                 type = va_arg(ap, require_type);
897                 size = va_arg(ap, int);
898                 /* Check if the field matches the criteria */
899                 AST_RWLIST_TRAVERSE(&tableptr->columns, col, list) {
900                         if (strcmp(col->name, elm) == 0) {
901                                 /* Type check, first.  Some fields are more particular than others */
902                                 switch (col->type) {
903                                 case SQL_CHAR:
904                                 case SQL_VARCHAR:
905                                 case SQL_LONGVARCHAR:
906                                 case SQL_BINARY:
907                                 case SQL_VARBINARY:
908                                 case SQL_LONGVARBINARY:
909                                 case SQL_GUID:
910                                         if ((type == RQ_INTEGER && size > 10) || (type == RQ_CHAR && col->size < size)) {
911                                                 warn_length(col, size);
912                                         } else if (type == RQ_DATE && col->size < 10) {
913                                                 warn_length(col, 10);
914                                         } else if (type == RQ_DATETIME && col->size < 19) {
915                                                 warn_length(col, 19);
916                                         } else if (type == RQ_FLOAT && col->size < 10) {
917                                                 warn_length(col, 10);
918                                         }
919                                         break;
920                                 case SQL_TYPE_DATE:
921                                         if (type != RQ_DATE) {
922                                                 warn_type(col, type);
923                                         }
924                                         break;
925                                 case SQL_TYPE_TIMESTAMP:
926                                 case SQL_TIMESTAMP:
927                                         if (type != RQ_DATE && type != RQ_DATETIME) {
928                                                 warn_type(col, type);
929                                         }
930                                         break;
931                                 case SQL_INTEGER:
932                                 case SQL_BIGINT:
933                                 case SQL_SMALLINT:
934                                 case SQL_TINYINT:
935                                 case SQL_BIT:
936                                         if (type != RQ_INTEGER) {
937                                                 warn_type(col, type);
938                                         }
939                                         if ((col->type == SQL_BIT && size > 1) ||
940                                                 (col->type == SQL_TINYINT && size > 2) ||
941                                                 (col->type == SQL_SMALLINT && size > 4) ||
942                                                 (col->type == SQL_INTEGER && size > 10)) {
943                                                 warn_length(col, size);
944                                         }
945                                         break;
946                                 case SQL_NUMERIC:
947                                 case SQL_DECIMAL:
948                                 case SQL_FLOAT:
949                                 case SQL_REAL:
950                                 case SQL_DOUBLE:
951                                         if (type != RQ_INTEGER && type != RQ_FLOAT) {
952                                                 warn_type(col, type);
953                                         }
954                                         break;
955                                 default:
956                                         ast_log(LOG_WARNING, "Column type (%d) unrecognized for field '%s' in %s@%s\n", col->type, elm, table, database);
957                                 }
958                                 break;
959                         }
960                 }
961                 if (!col) {
962                         ast_log(LOG_WARNING, "Table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
963                 }
964         }
965         va_end(ap);
966         AST_RWLIST_UNLOCK(&tableptr->columns);
967         return 0;
968 }
969
970 static int unload_odbc(const char *database, const char *tablename)
971 {
972         struct odbc_cache_tables *tableptr;
973
974         AST_RWLIST_RDLOCK(&odbc_tables);
975         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&odbc_tables, tableptr, list) {
976                 if (strcmp(tableptr->connection, database) == 0 && strcmp(tableptr->table, tablename) == 0) {
977                         AST_LIST_REMOVE_CURRENT(list);
978                         destroy_table_cache(tableptr);
979                         break;
980                 }
981         }
982         AST_RWLIST_TRAVERSE_SAFE_END
983         AST_RWLIST_UNLOCK(&odbc_tables);
984         return tableptr ? 0 : -1;
985 }
986
987 static struct ast_config_engine odbc_engine = {
988         .name = "odbc",
989         .load_func = config_odbc,
990         .realtime_func = realtime_odbc,
991         .realtime_multi_func = realtime_multi_odbc,
992         .store_func = store_odbc,
993         .destroy_func = destroy_odbc,
994         .update_func = update_odbc,
995         .require_func = require_odbc,
996         .unload_func = unload_odbc,
997 };
998
999 static int unload_module (void)
1000 {
1001         struct odbc_cache_tables *table;
1002
1003         ast_config_engine_deregister(&odbc_engine);
1004
1005         /* Empty the cache */
1006         AST_RWLIST_WRLOCK(&odbc_tables);
1007         while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
1008                 destroy_table_cache(table);
1009         }
1010         AST_RWLIST_UNLOCK(&odbc_tables);
1011
1012         ast_verb(1, "res_config_odbc unloaded.\n");
1013         return 0;
1014 }
1015
1016 static int load_module (void)
1017 {
1018         ast_config_engine_register(&odbc_engine);
1019         ast_verb(1, "res_config_odbc loaded.\n");
1020         return 0;
1021 }
1022
1023 static int reload_module(void)
1024 {
1025         struct odbc_cache_tables *table;
1026
1027         /* Empty the cache; it will get rebuilt the next time the tables are needed. */
1028         AST_RWLIST_WRLOCK(&odbc_tables);
1029         while ((table = AST_RWLIST_REMOVE_HEAD(&odbc_tables, list))) {
1030                 destroy_table_cache(table);
1031         }
1032         AST_RWLIST_UNLOCK(&odbc_tables);
1033
1034         return 0;
1035 }
1036
1037 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Realtime ODBC configuration",
1038                 .load = load_module,
1039                 .unload = unload_module,
1040                 .reload = reload_module,
1041                 );