0b1d5a14a6ca5de9f96bcefe433b2efee9d746c4
[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>res_odbc</depend>
33  ***/
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include "asterisk/file.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/config.h"
43 #include "asterisk/module.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/res_odbc.h"
46 #include "asterisk/utils.h"
47
48 AST_THREADSTORAGE(sql_buf);
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 static SQLHSTMT custom_prepare(struct odbc_obj *obj, void *data)
58 {
59         int res, x = 1, count = 0;
60         struct custom_prepare_struct *cps = data;
61         const char *newparam, *newval;
62         SQLHSTMT stmt;
63         va_list ap;
64
65         va_copy(ap, cps->ap);
66
67         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
68         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
69                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
70                 return NULL;
71         }
72
73         ast_debug(1, "Skip: %lld; SQL: %s\n", cps->skip, cps->sql);
74
75         res = SQLPrepare(stmt, (unsigned char *)cps->sql, SQL_NTS);
76         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
77                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", cps->sql);
78                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
79                 return NULL;
80         }
81
82         while ((newparam = va_arg(ap, const char *))) {
83                 newval = va_arg(ap, const char *);
84                 if ((1LL << count++) & cps->skip) {
85                         ast_debug(1, "Skipping field '%s'='%s' (%llo/%llo)\n", newparam, newval, 1LL << (count - 1), cps->skip);
86                         continue;
87                 }
88                 ast_debug(1, "Parameter %d ('%s') = '%s'\n", x, newparam, newval);
89                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
90         }
91         va_end(ap);
92
93         if (!ast_strlen_zero(cps->extra))
94                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(cps->extra), 0, (void *)cps->extra, 0, NULL);
95         return stmt;
96 }
97
98 /*!
99  * \brief Excute an SQL query and return ast_variable list
100  * \param database
101  * \param table
102  * \param ap list containing one or more field/operator/value set.
103  *
104  * Select database and preform query on table, prepare the sql statement
105  * Sub-in the values to the prepared statement and execute it. Return results
106  * as a ast_variable list.
107  *
108  * \retval var on success
109  * \retval NULL on failure
110 */
111 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
112 {
113         struct odbc_obj *obj;
114         SQLHSTMT stmt;
115         char sql[1024];
116         char coltitle[256];
117         char rowdata[2048];
118         char *op;
119         const char *newparam, *newval;
120         char *stringp;
121         char *chunk;
122         SQLSMALLINT collen;
123         int res;
124         int x;
125         struct ast_variable *var=NULL, *prev=NULL;
126         SQLULEN colsize;
127         SQLSMALLINT colcount=0;
128         SQLSMALLINT datatype;
129         SQLSMALLINT decimaldigits;
130         SQLSMALLINT nullable;
131         SQLLEN indicator;
132         va_list aq;
133         struct custom_prepare_struct cps = { .sql = sql };
134
135         va_copy(cps.ap, ap);
136         va_copy(aq, ap);
137
138         if (!table)
139                 return NULL;
140
141         obj = ast_odbc_request_obj(database, 0);
142
143         if (!obj) {
144                 ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", database);
145                 return NULL;
146         }
147
148         newparam = va_arg(aq, const char *);
149         if (!newparam) {
150                 ast_odbc_release_obj(obj);
151                 return NULL;
152         }
153         newval = va_arg(aq, const char *);
154         op = !strchr(newparam, ' ') ? " =" : "";
155         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
156                 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
157         while((newparam = va_arg(aq, const char *))) {
158                 op = !strchr(newparam, ' ') ? " =" : "";
159                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
160                         strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
161                 newval = va_arg(aq, const char *);
162         }
163         va_end(aq);
164
165         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
166
167         if (!stmt) {
168                 ast_odbc_release_obj(obj);
169                 return NULL;
170         }
171
172         res = SQLNumResultCols(stmt, &colcount);
173         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
174                 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
175                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
176                 ast_odbc_release_obj(obj);
177                 return NULL;
178         }
179
180         res = SQLFetch(stmt);
181         if (res == SQL_NO_DATA) {
182                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
183                 ast_odbc_release_obj(obj);
184                 return NULL;
185         }
186         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
187                 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
188                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
189                 ast_odbc_release_obj(obj);
190                 return NULL;
191         }
192         for (x = 0; x < colcount; x++) {
193                 rowdata[0] = '\0';
194                 collen = sizeof(coltitle);
195                 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
196                                         &datatype, &colsize, &decimaldigits, &nullable);
197                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
198                         ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
199                         if (var)
200                                 ast_variables_destroy(var);
201                         ast_odbc_release_obj(obj);
202                         return NULL;
203                 }
204
205                 indicator = 0;
206                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
207                 if (indicator == SQL_NULL_DATA)
208                         rowdata[0] = '\0';
209                 else if (ast_strlen_zero(rowdata)) {
210                         /* Because we encode the empty string for a NULL, we will encode
211                          * actual empty strings as a string containing a single whitespace. */
212                         ast_copy_string(rowdata, " ", sizeof(rowdata));
213                 }
214
215                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
216                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
217                         if (var)
218                                 ast_variables_destroy(var);
219                         ast_odbc_release_obj(obj);
220                         return NULL;
221                 }
222                 stringp = rowdata;
223                 while(stringp) {
224                         chunk = strsep(&stringp, ";");
225                         if (!ast_strlen_zero(ast_strip(chunk))) {
226                                 if (prev) {
227                                         prev->next = ast_variable_new(coltitle, chunk, "");
228                                         if (prev->next)
229                                                 prev = prev->next;
230                                 } else 
231                                         prev = var = ast_variable_new(coltitle, chunk, "");
232                         }
233                 }
234         }
235
236
237         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
238         ast_odbc_release_obj(obj);
239         return var;
240 }
241
242 /*!
243  * \brief Excute an Select query and return ast_config list
244  * \param database
245  * \param table
246  * \param ap list containing one or more field/operator/value set.
247  *
248  * Select database and preform query on table, prepare the sql statement
249  * Sub-in the values to the prepared statement and execute it. 
250  * Execute this prepared query against several ODBC connected databases.
251  * Return results as an ast_config variable.
252  *
253  * \retval var on success
254  * \retval NULL on failure
255 */
256 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
257 {
258         struct odbc_obj *obj;
259         SQLHSTMT stmt;
260         char sql[1024];
261         char coltitle[256];
262         char rowdata[2048];
263         const char *initfield=NULL;
264         char *op;
265         const char *newparam, *newval;
266         char *stringp;
267         char *chunk;
268         SQLSMALLINT collen;
269         int res;
270         int x;
271         struct ast_variable *var=NULL;
272         struct ast_config *cfg=NULL;
273         struct ast_category *cat=NULL;
274         SQLULEN colsize;
275         SQLSMALLINT colcount=0;
276         SQLSMALLINT datatype;
277         SQLSMALLINT decimaldigits;
278         SQLSMALLINT nullable;
279         SQLLEN indicator;
280         struct custom_prepare_struct cps = { .sql = sql };
281         va_list aq;
282
283         va_copy(cps.ap, ap);
284         va_copy(aq, ap);
285
286         if (!table)
287                 return NULL;
288
289         obj = ast_odbc_request_obj(database, 0);
290         if (!obj)
291                 return NULL;
292
293         newparam = va_arg(aq, const char *);
294         if (!newparam)  {
295                 ast_odbc_release_obj(obj);
296                 return NULL;
297         }
298         initfield = ast_strdupa(newparam);
299         if ((op = strchr(initfield, ' '))) 
300                 *op = '\0';
301         newval = va_arg(aq, const char *);
302         op = !strchr(newparam, ' ') ? " =" : "";
303         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?%s", table, newparam, op,
304                 strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
305         while((newparam = va_arg(aq, const char *))) {
306                 op = !strchr(newparam, ' ') ? " =" : "";
307                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?%s", newparam, op,
308                         strcasestr(newparam, "LIKE") && !ast_odbc_backslash_is_escape(obj) ? " ESCAPE '\\'" : "");
309                 newval = va_arg(aq, const char *);
310         }
311         if (initfield)
312                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
313         va_end(aq);
314
315         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
316
317         if (!stmt) {
318                 ast_odbc_release_obj(obj);
319                 return NULL;
320         }
321
322         res = SQLNumResultCols(stmt, &colcount);
323         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
324                 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
325                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
326                 ast_odbc_release_obj(obj);
327                 return NULL;
328         }
329
330         cfg = ast_config_new();
331         if (!cfg) {
332                 ast_log(LOG_WARNING, "Out of memory!\n");
333                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
334                 ast_odbc_release_obj(obj);
335                 return NULL;
336         }
337
338         while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
339                 var = NULL;
340                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
341                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
342                         continue;
343                 }
344                 cat = ast_category_new("","",99999);
345                 if (!cat) {
346                         ast_log(LOG_WARNING, "Out of memory!\n");
347                         continue;
348                 }
349                 for (x=0;x<colcount;x++) {
350                         rowdata[0] = '\0';
351                         collen = sizeof(coltitle);
352                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
353                                                 &datatype, &colsize, &decimaldigits, &nullable);
354                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
355                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
356                                 ast_category_destroy(cat);
357                                 continue;
358                         }
359
360                         indicator = 0;
361                         res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
362                         if (indicator == SQL_NULL_DATA)
363                                 continue;
364
365                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
366                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
367                                 ast_category_destroy(cat);
368                                 continue;
369                         }
370                         stringp = rowdata;
371                         while(stringp) {
372                                 chunk = strsep(&stringp, ";");
373                                 if (!ast_strlen_zero(ast_strip(chunk))) {
374                                         if (initfield && !strcmp(initfield, coltitle))
375                                                 ast_category_rename(cat, chunk);
376                                         var = ast_variable_new(coltitle, chunk, "");
377                                         ast_variable_append(cat, var);
378                                 }
379                         }
380                 }
381                 ast_category_append(cfg, cat);
382         }
383
384         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
385         ast_odbc_release_obj(obj);
386         return cfg;
387 }
388
389 /*!
390  * \brief Excute an UPDATE query
391  * \param database
392  * \param table
393  * \param keyfield where clause field
394  * \param lookup value of field for where clause
395  * \param ap list containing one or more field/value set(s).
396  *
397  * Update a database table, prepare the sql statement using keyfield and lookup
398  * control the number of records to change. All values to be changed are stored in ap list.
399  * Sub-in the values to the prepared statement and execute it.
400  *
401  * \retval number of rows affected
402  * \retval -1 on failure
403 */
404 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
405 {
406         struct odbc_obj *obj;
407         SQLHSTMT stmt;
408         char sql[256];
409         SQLLEN rowcount=0;
410         const char *newparam, *newval;
411         int res, count = 1;
412         va_list aq;
413         struct custom_prepare_struct cps = { .sql = sql, .extra = lookup };
414         struct odbc_cache_tables *tableptr = ast_odbc_find_table(database, table);
415         struct odbc_cache_columns *column;
416
417         va_copy(cps.ap, ap);
418         va_copy(aq, ap);
419         
420         if (!table) {
421                 ast_odbc_release_table(tableptr);
422                 return -1;
423         }
424
425         obj = ast_odbc_request_obj(database, 0);
426         if (!obj) {
427                 ast_odbc_release_table(tableptr);
428                 return -1;
429         }
430
431         newparam = va_arg(aq, const char *);
432         if (!newparam)  {
433                 ast_odbc_release_obj(obj);
434                 ast_odbc_release_table(tableptr);
435                 return -1;
436         }
437         newval = va_arg(aq, const char *);
438
439         if (tableptr && !(column = ast_odbc_find_column(tableptr, newparam))) {
440                 ast_log(LOG_WARNING, "Key field '%s' does not exist in table '%s@%s'.  Update will fail\n", newparam, table, database);
441         }
442
443         snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
444         while((newparam = va_arg(aq, const char *))) {
445                 newval = va_arg(aq, const char *);
446                 if ((tableptr && (column = ast_odbc_find_column(tableptr, newparam))) || count > 63) {
447                         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
448                 } else { /* the column does not exist in the table */
449                         cps.skip |= (1LL << count);
450                 }
451                 count++;
452         }
453         va_end(aq);
454         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
455         ast_odbc_release_table(tableptr);
456
457         stmt = ast_odbc_prepare_and_execute(obj, custom_prepare, &cps);
458
459         if (!stmt) {
460                 ast_odbc_release_obj(obj);
461                 return -1;
462         }
463
464         res = SQLRowCount(stmt, &rowcount);
465         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
466         ast_odbc_release_obj(obj);
467
468         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
469                 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
470                 return -1;
471         }
472
473         if (rowcount >= 0)
474                 return (int)rowcount;
475
476         return -1;
477 }
478
479 struct update2_prepare_struct {
480         const char *database;
481         const char *table;
482         va_list ap;
483 };
484
485 static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data)
486 {
487         int res, x = 1, first = 1;
488         struct update2_prepare_struct *ups = data;
489         const char *newparam, *newval;
490         struct ast_str *sql = ast_str_thread_get(&sql_buf, 16);
491         SQLHSTMT stmt;
492         va_list ap;
493         struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table);
494         struct odbc_cache_columns *column;
495
496         if (!sql) {
497                 if (tableptr) {
498                         ast_odbc_release_table(tableptr);
499                 }
500                 return NULL;
501         }
502
503         if (!tableptr) {
504                 ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'.  Update will fail!\n", ups->table, ups->database);
505                 return NULL;
506         }
507
508         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
509         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
510                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
511                 ast_odbc_release_table(tableptr);
512                 return NULL;
513         }
514
515         ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table);
516
517         /* Start by finding the second set of parameters */
518         va_copy(ap, ups->ap);
519
520         while ((newparam = va_arg(ap, const char *))) {
521                 newval = va_arg(ap, const char *);
522         }
523
524         while ((newparam = va_arg(ap, const char *))) {
525                 newval = va_arg(ap, const char *);
526                 if ((column = ast_odbc_find_column(tableptr, newparam))) {
527                         ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam);
528                         SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
529                         first = 0;
530                 } else {
531                         ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database);
532                 }
533         }
534         va_end(ap);
535
536         /* Restart search, because we need to add the search parameters */
537         va_copy(ap, ups->ap);
538         ast_str_append(&sql, 0, "WHERE");
539         first = 1;
540
541         while ((newparam = va_arg(ap, const char *))) {
542                 newval = va_arg(ap, const char *);
543                 if (!(column = ast_odbc_find_column(tableptr, newparam))) {
544                         ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", newparam, ups->table, ups->database);
545                         ast_odbc_release_table(tableptr);
546                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
547                         return NULL;
548                 }
549                 ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam);
550                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
551                 first = 0;
552         }
553         va_end(ap);
554
555         /* Done with the table metadata */
556         ast_odbc_release_table(tableptr);
557
558         res = SQLPrepare(stmt, (unsigned char *)ast_str_buffer(sql), SQL_NTS);
559         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
560                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", ast_str_buffer(sql));
561                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
562                 return NULL;
563         }
564
565         return stmt;
566 }
567
568 /*!
569  * \brief Execute an UPDATE query
570  * \param database
571  * \param table
572  * \param ap list containing one or more field/value set(s).
573  *
574  * Update a database table, preparing the sql statement from a list of
575  * key/value pairs specified in ap.  The lookup pairs are specified first
576  * and are separated from the update pairs by a sentinel value.
577  * Sub-in the values to the prepared statement and execute it.
578  *
579  * \retval number of rows affected
580  * \retval -1 on failure
581 */
582 static int update2_odbc(const char *database, const char *table, va_list ap)
583 {
584         struct odbc_obj *obj;
585         SQLHSTMT stmt;
586         struct update2_prepare_struct ups = { .database = database, .table = table, };
587         struct ast_str *sql;
588         int res;
589         SQLLEN rowcount = 0;
590
591         va_copy(ups.ap, ap);
592
593         if (!(obj = ast_odbc_request_obj(database, 0))) {
594                 return -1;
595         }
596
597         if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) {
598                 ast_odbc_release_obj(obj);
599                 return -1;
600         }
601
602         res = SQLRowCount(stmt, &rowcount);
603         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
604         ast_odbc_release_obj(obj);
605
606         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
607                 /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */
608                 sql = ast_str_thread_get(&sql_buf, 16);
609                 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", ast_str_buffer(sql));
610                 return -1;
611         }
612
613         if (rowcount >= 0) {
614                 return (int)rowcount;
615         }
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, "Realtime table %s@%s: column '%s' is not long enough to contain realtime data (needs %d)\n", table, database, col->name, size)
882 #define warn_type(col, type)    ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' is of the incorrect type (%d) to contain the required realtime data\n", table, database, col->name, col->type)
883
884 static int require_odbc(const char *database, const char *table, va_list ap)
885 {
886         struct odbc_cache_tables *tableptr = ast_odbc_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 #ifdef HAVE_ODBC_WCHAR
907                                 case SQL_WCHAR:
908                                 case SQL_WVARCHAR:
909                                 case SQL_WLONGVARCHAR:
910 #endif
911                                 case SQL_BINARY:
912                                 case SQL_VARBINARY:
913                                 case SQL_LONGVARBINARY:
914                                 case SQL_GUID:
915 #define CHECK_SIZE(n) \
916                                                 if (col->size < n) {      \
917                                                         warn_length(col, n);  \
918                                                 }                         \
919                                                 break;
920                                         switch (type) {
921                                         case RQ_UINTEGER1: CHECK_SIZE(3)  /*         255 */
922                                         case RQ_INTEGER1:  CHECK_SIZE(4)  /*        -128 */
923                                         case RQ_UINTEGER2: CHECK_SIZE(5)  /*       65535 */
924                                         case RQ_INTEGER2:  CHECK_SIZE(6)  /*      -32768 */
925                                         case RQ_UINTEGER3:                /*    16777215 */
926                                         case RQ_INTEGER3:  CHECK_SIZE(8)  /*    -8388608 */
927                                         case RQ_DATE:                     /*  2008-06-09 */
928                                         case RQ_UINTEGER4: CHECK_SIZE(10) /*  4200000000 */
929                                         case RQ_INTEGER4:  CHECK_SIZE(11) /* -2100000000 */
930                                         case RQ_DATETIME:                 /* 2008-06-09 16:03:47 */
931                                         case RQ_UINTEGER8: CHECK_SIZE(19) /* trust me    */
932                                         case RQ_INTEGER8:  CHECK_SIZE(20) /* ditto       */
933                                         case RQ_FLOAT:
934                                         case RQ_CHAR:      CHECK_SIZE(size)
935                                         }
936 #undef CHECK_SIZE
937                                         break;
938                                 case SQL_TYPE_DATE:
939                                         if (type != RQ_DATE) {
940                                                 warn_type(col, type);
941                                         }
942                                         break;
943                                 case SQL_TYPE_TIMESTAMP:
944                                 case SQL_TIMESTAMP:
945                                         if (type != RQ_DATE && type != RQ_DATETIME) {
946                                                 warn_type(col, type);
947                                         }
948                                         break;
949                                 case SQL_BIT:
950                                         warn_length(col, size);
951                                         break;
952 #define WARN_TYPE_OR_LENGTH(n)  \
953                                                 if (!ast_rq_is_int(type)) {  \
954                                                         warn_type(col, type);    \
955                                                 } else {                     \
956                                                         warn_length(col, n);  \
957                                                 }
958                                 case SQL_TINYINT:
959                                         if (type != RQ_UINTEGER1) {
960                                                 WARN_TYPE_OR_LENGTH(size)
961                                         }
962                                         break;
963                                 case SQL_C_STINYINT:
964                                         if (type != RQ_INTEGER1) {
965                                                 WARN_TYPE_OR_LENGTH(size)
966                                         }
967                                         break;
968                                 case SQL_C_USHORT:
969                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) {
970                                                 WARN_TYPE_OR_LENGTH(size)
971                                         }
972                                         break;
973                                 case SQL_SMALLINT:
974                                 case SQL_C_SSHORT:
975                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) {
976                                                 WARN_TYPE_OR_LENGTH(size)
977                                         }
978                                         break;
979                                 case SQL_C_ULONG:
980                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
981                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
982                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
983                                                 type != RQ_INTEGER4) {
984                                                 WARN_TYPE_OR_LENGTH(size)
985                                         }
986                                         break;
987                                 case SQL_INTEGER:
988                                 case SQL_C_SLONG:
989                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
990                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
991                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
992                                                 type != RQ_INTEGER4) {
993                                                 WARN_TYPE_OR_LENGTH(size)
994                                         }
995                                         break;
996                                 case SQL_C_UBIGINT:
997                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
998                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
999                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1000                                                 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1001                                                 type != RQ_INTEGER8) {
1002                                                 WARN_TYPE_OR_LENGTH(size)
1003                                         }
1004                                         break;
1005                                 case SQL_BIGINT:
1006                                 case SQL_C_SBIGINT:
1007                                         if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 &&
1008                                                 type != RQ_UINTEGER2 && type != RQ_INTEGER2 &&
1009                                                 type != RQ_UINTEGER3 && type != RQ_INTEGER3 &&
1010                                                 type != RQ_UINTEGER4 && type != RQ_INTEGER4 &&
1011                                                 type != RQ_INTEGER8) {
1012                                                 WARN_TYPE_OR_LENGTH(size)
1013                                         }
1014                                         break;
1015 #undef WARN_TYPE_OR_LENGTH
1016                                 case SQL_NUMERIC:
1017                                 case SQL_DECIMAL:
1018                                 case SQL_FLOAT:
1019                                 case SQL_REAL:
1020                                 case SQL_DOUBLE:
1021                                         if (!ast_rq_is_int(type) && type != RQ_FLOAT) {
1022                                                 warn_type(col, type);
1023                                         }
1024                                         break;
1025                                 default:
1026                                         ast_log(LOG_WARNING, "Realtime table %s@%s: column type (%d) unrecognized for column '%s'\n", table, database, col->type, elm);
1027                                 }
1028                                 break;
1029                         }
1030                 }
1031                 if (!col) {
1032                         ast_log(LOG_WARNING, "Realtime table %s@%s requires column '%s', but that column does not exist!\n", table, database, elm);
1033                 }
1034         }
1035         va_end(ap);
1036         AST_RWLIST_UNLOCK(&tableptr->columns);
1037         return 0;
1038 }
1039 #undef warn_length
1040 #undef warn_type
1041
1042 static struct ast_config_engine odbc_engine = {
1043         .name = "odbc",
1044         .load_func = config_odbc,
1045         .realtime_func = realtime_odbc,
1046         .realtime_multi_func = realtime_multi_odbc,
1047         .store_func = store_odbc,
1048         .destroy_func = destroy_odbc,
1049         .update_func = update_odbc,
1050         .update2_func = update2_odbc,
1051         .require_func = require_odbc,
1052         .unload_func = ast_odbc_clear_cache,
1053 };
1054
1055 static int unload_module (void)
1056 {
1057         ast_config_engine_deregister(&odbc_engine);
1058
1059         ast_verb(1, "res_config_odbc unloaded.\n");
1060         return 0;
1061 }
1062
1063 static int load_module (void)
1064 {
1065         ast_config_engine_register(&odbc_engine);
1066         ast_verb(1, "res_config_odbc loaded.\n");
1067         return 0;
1068 }
1069
1070 static int reload_module(void)
1071 {
1072         return 0;
1073 }
1074
1075 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Realtime ODBC configuration",
1076                 .load = load_module,
1077                 .unload = unload_module,
1078                 .reload = reload_module,
1079                 );