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