issue #5648
[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  *      http://www.unixodbc.org
26  */
27
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include "asterisk/file.h"
37 #include "asterisk/logger.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/config.h"
41 #include "asterisk/module.h"
42 #include "asterisk/lock.h"
43 #include "asterisk/options.h"
44 #include "asterisk/res_odbc.h"
45 #include "asterisk/utils.h"
46
47 static char *tdesc = "ODBC Configuration";
48 STANDARD_LOCAL_USER;
49
50 LOCAL_USER_DECL;
51
52 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
53 {
54         odbc_obj *obj;
55         SQLHSTMT stmt;
56         char sql[1024];
57         char coltitle[256];
58         char rowdata[2048];
59         char *op;
60         const char *newparam, *newval;
61         char *stringp;
62         char *chunk;
63         SQLSMALLINT collen;
64         int res;
65         int x;
66         struct ast_variable *var=NULL, *prev=NULL;
67         SQLULEN colsize;
68         SQLSMALLINT colcount=0;
69         SQLSMALLINT datatype;
70         SQLSMALLINT decimaldigits;
71         SQLSMALLINT nullable;
72         SQLINTEGER indicator;
73         va_list aq;
74         
75         va_copy(aq, ap);
76         
77         
78         if (!table)
79                 return NULL;
80
81         obj = fetch_odbc_obj(database, 0);
82         if (!obj)
83                 return NULL;
84
85         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
86         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
87                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
88                 return NULL;
89         }
90
91         newparam = va_arg(aq, const char *);
92         if (!newparam)  {
93                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
94                 return NULL;
95         }
96         newval = va_arg(aq, const char *);
97         if (!strchr(newparam, ' ')) op = " ="; else op = "";
98         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?", table, newparam, op);
99         while((newparam = va_arg(aq, const char *))) {
100                 if (!strchr(newparam, ' ')) op = " ="; else op = "";
101                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?", newparam, op);
102                 newval = va_arg(aq, const char *);
103         }
104         va_end(aq);
105         res = SQLPrepare(stmt, sql, SQL_NTS);
106         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
107                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
108                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
109                 return NULL;
110         }
111         
112         /* Now bind the parameters */
113         x = 1;
114
115         while((newparam = va_arg(ap, const char *))) {
116                 newval = va_arg(ap, const char *);
117                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
118         }
119         
120         res = odbc_smart_execute(obj, stmt);
121
122         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
123                 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
124                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
125                 return NULL;
126         }
127
128         res = SQLNumResultCols(stmt, &colcount);
129         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
130                 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
131                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
132                 return NULL;
133         }
134
135         res = SQLFetch(stmt);
136         if (res == SQL_NO_DATA) {
137                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
138                 return NULL;
139         }
140         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
141                 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
142                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
143                 return NULL;
144         }
145         for (x=0;x<colcount;x++) {
146                 rowdata[0] = '\0';
147                 collen = sizeof(coltitle);
148                 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
149                                         &datatype, &colsize, &decimaldigits, &nullable);
150                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
151                         ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
152                         if (var)
153                                 ast_variables_destroy(var);
154                         return NULL;
155                 }
156
157                 indicator = 0;
158                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
159                 if (indicator == SQL_NULL_DATA)
160                         continue;
161
162                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
163                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
164                         if (var)
165                                 ast_variables_destroy(var);
166                         return NULL;
167                 }
168                 stringp = rowdata;
169                 while(stringp) {
170                         chunk = strsep(&stringp, ";");
171                         if (!ast_strlen_zero(ast_strip(chunk))) {
172                                 if (prev) {
173                                         prev->next = ast_variable_new(coltitle, chunk);
174                                         if (prev->next)
175                                                 prev = prev->next;
176                                         } else 
177                                                 prev = var = ast_variable_new(coltitle, chunk);
178                                         
179                         }
180                 }
181         }
182
183
184         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
185         return var;
186 }
187
188 static struct ast_config *realtime_multi_odbc(const char *database, const char *table, va_list ap)
189 {
190         odbc_obj *obj;
191         SQLHSTMT stmt;
192         char sql[1024];
193         char coltitle[256];
194         char rowdata[2048];
195         const char *initfield=NULL;
196         char *op;
197         const char *newparam, *newval;
198         char *stringp;
199         char *chunk;
200         SQLSMALLINT collen;
201         int res;
202         int x;
203         struct ast_variable *var=NULL;
204         struct ast_config *cfg=NULL;
205         struct ast_category *cat=NULL;
206         struct ast_realloca ra;
207         SQLULEN colsize;
208         SQLSMALLINT colcount=0;
209         SQLSMALLINT datatype;
210         SQLSMALLINT decimaldigits;
211         SQLSMALLINT nullable;
212         SQLINTEGER indicator;
213
214         va_list aq;
215         va_copy(aq, ap);
216         
217         
218         if (!table)
219                 return NULL;
220         memset(&ra, 0, sizeof(ra));
221
222         obj = fetch_odbc_obj(database, 0);
223         if (!obj)
224                 return NULL;
225
226         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
227         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
228                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
229                 return NULL;
230         }
231
232         newparam = va_arg(aq, const char *);
233         if (!newparam)  {
234                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
235                 return NULL;
236         }
237         initfield = ast_strdupa(newparam);
238         if (initfield && (op = strchr(initfield, ' '))) 
239                 *op = '\0';
240         newval = va_arg(aq, const char *);
241         if (!strchr(newparam, ' ')) op = " ="; else op = "";
242         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s ?", table, newparam, op);
243         while((newparam = va_arg(aq, const char *))) {
244                 if (!strchr(newparam, ' ')) op = " ="; else op = "";
245                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s ?", newparam, op);
246                 newval = va_arg(aq, const char *);
247         }
248         if (initfield)
249                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield);
250         va_end(aq);
251         res = SQLPrepare(stmt, sql, SQL_NTS);
252         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
253                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
254                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
255                 return NULL;
256         }
257         
258         /* Now bind the parameters */
259         x = 1;
260
261         while((newparam = va_arg(ap, const char *))) {
262                 newval = va_arg(ap, const char *);
263                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
264         }
265                 
266         res = odbc_smart_execute(obj, stmt);
267
268         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
269                 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
270                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
271                 return NULL;
272         }
273
274         res = SQLNumResultCols(stmt, &colcount);
275         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
276                 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
277                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
278                 return NULL;
279         }
280
281         cfg = ast_config_new();
282         if (!cfg) {
283                 ast_log(LOG_WARNING, "Out of memory!\n");
284                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
285                 return NULL;
286         }
287
288         while ((res=SQLFetch(stmt)) != SQL_NO_DATA) {
289                 var = NULL;
290                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
291                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
292                         continue;
293                 }
294                 cat = ast_category_new("");
295                 if (!cat) {
296                         ast_log(LOG_WARNING, "Out of memory!\n");
297                         continue;
298                 }
299                 for (x=0;x<colcount;x++) {
300                         rowdata[0] = '\0';
301                         collen = sizeof(coltitle);
302                         res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
303                                                 &datatype, &colsize, &decimaldigits, &nullable);
304                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
305                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
306                                 ast_category_destroy(cat);
307                                 continue;
308                         }
309
310                         indicator = 0;
311                         res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator);
312                         if (indicator == SQL_NULL_DATA)
313                                 continue;
314
315                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
316                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
317                                 ast_category_destroy(cat);
318                                 continue;
319                         }
320                         stringp = rowdata;
321                         while(stringp) {
322                                 chunk = strsep(&stringp, ";");
323                                 if (!ast_strlen_zero(ast_strip(chunk))) {
324                                         if (initfield && !strcmp(initfield, coltitle))
325                                                 ast_category_rename(cat, chunk);
326                                         var = ast_variable_new(coltitle, chunk);
327                                         ast_variable_append(cat, var);
328                                 }
329                         }
330                 }
331                 ast_category_append(cfg, cat);
332         }
333
334         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
335         return cfg;
336 }
337
338 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
339 {
340         odbc_obj *obj;
341         SQLHSTMT stmt;
342         char sql[256];
343         SQLLEN rowcount=0;
344         const char *newparam, *newval;
345         int res;
346         int x;
347         va_list aq;
348         
349         va_copy(aq, ap);
350         
351         if (!table)
352                 return -1;
353
354         obj = fetch_odbc_obj (database, 0);
355         if (!obj)
356                 return -1;
357
358         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
359         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
360                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
361                 return -1;
362         }
363
364         newparam = va_arg(aq, const char *);
365         if (!newparam)  {
366                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
367                 return -1;
368         }
369         newval = va_arg(aq, const char *);
370         snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
371         while((newparam = va_arg(aq, const char *))) {
372                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
373                 newval = va_arg(aq, const char *);
374         }
375         va_end(aq);
376         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
377         
378         res = SQLPrepare(stmt, sql, SQL_NTS);
379         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
380                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
381                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
382                 return -1;
383         }
384         
385         /* Now bind the parameters */
386         x = 1;
387
388         while((newparam = va_arg(ap, const char *))) {
389                 newval = va_arg(ap, const char *);
390                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
391         }
392                 
393         SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(lookup), 0, (void *)lookup, 0, NULL);
394
395         res = odbc_smart_execute(obj, stmt);
396
397         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
398                 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
399                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
400                 return -1;
401         }
402
403         res = SQLRowCount(stmt, &rowcount);
404         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
405
406         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
407                 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
408                 return -1;
409         }
410
411        if (rowcount >= 0)
412                return (int)rowcount;
413
414         return -1;
415 }
416
417 static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg)
418 {
419         struct ast_variable *new_v;
420         struct ast_category *cur_cat;
421         int res = 0;
422         odbc_obj *obj;
423         SQLINTEGER err=0, commented=0, cat_metric=0, var_metric=0, last_cat_metric=0;
424         SQLBIGINT id;
425         char sql[255] = "", filename[128], category[128], var_name[128], var_val[512];
426         SQLSMALLINT rowcount=0;
427         SQLHSTMT stmt;
428         char last[128] = "";
429
430         if (!file || !strcmp (file, "res_config_odbc.conf"))
431                 return NULL;            /* cant configure myself with myself ! */
432
433         obj = fetch_odbc_obj(database, 0);
434         if (!obj)
435                 return NULL;
436
437         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
438
439         SQLBindCol (stmt, 1, SQL_C_ULONG, &id, sizeof (id), &err);
440         SQLBindCol (stmt, 2, SQL_C_ULONG, &cat_metric, sizeof (cat_metric), &err);
441         SQLBindCol (stmt, 3, SQL_C_ULONG, &var_metric, sizeof (var_metric), &err);
442         SQLBindCol (stmt, 4, SQL_C_ULONG, &commented, sizeof (commented), &err);
443         SQLBindCol (stmt, 5, SQL_C_CHAR, &filename, sizeof (filename), &err);
444         SQLBindCol (stmt, 6, SQL_C_CHAR, &category, sizeof (category), &err);
445         SQLBindCol (stmt, 7, SQL_C_CHAR, &var_name, sizeof (var_name), &err);
446         SQLBindCol (stmt, 8, SQL_C_CHAR, &var_val, sizeof (var_val), &err);
447         
448         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE filename='%s' and commented=0 ORDER BY filename,cat_metric desc,var_metric asc,category,var_name,var_val,id", table, file);
449
450         res = odbc_smart_direct_execute(obj, stmt, sql);
451         
452         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
453                 ast_log (LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
454                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
455                 return NULL;
456         }
457
458         res = SQLNumResultCols (stmt, &rowcount);
459
460         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
461                 ast_log (LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql);
462                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
463                 return NULL;
464         }
465
466         if (!rowcount) {
467                 ast_log (LOG_NOTICE, "found nothing\n");
468                 return cfg;
469         }
470
471         cur_cat = ast_config_get_current_category(cfg);
472
473         while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
474                 if (!strcmp (var_name, "#include")) {
475                         if (!ast_config_internal_load(var_val, cfg)) {
476                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
477                                 return NULL;
478                         }
479                         continue;
480                 } 
481                 if (strcmp(last, category) || last_cat_metric != cat_metric) {
482                         cur_cat = ast_category_new(category);
483                         if (!cur_cat) {
484                                 ast_log(LOG_WARNING, "Out of memory!\n");
485                                 break;
486                         }
487                         strcpy(last, category);
488                         last_cat_metric = cat_metric;
489                         ast_category_append(cfg, cur_cat);
490                 }
491
492                 new_v = ast_variable_new(var_name, var_val);
493                 ast_variable_append(cur_cat, new_v);
494         }
495
496         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
497         return cfg;
498 }
499
500 static struct ast_config_engine odbc_engine = {
501         .name = "odbc",
502         .load_func = config_odbc,
503         .realtime_func = realtime_odbc,
504         .realtime_multi_func = realtime_multi_odbc,
505         .update_func = update_odbc
506 };
507
508 int unload_module (void)
509 {
510         ast_config_engine_deregister(&odbc_engine);
511         if (option_verbose)
512                 ast_verbose("res_config_odbc unloaded.\n");
513         STANDARD_HANGUP_LOCALUSERS;
514         return 0;
515 }
516
517 int load_module (void)
518 {
519         ast_config_engine_register(&odbc_engine);
520         if (option_verbose)
521                 ast_verbose("res_config_odbc loaded.\n");
522         return 0;
523 }
524
525 char *description (void)
526 {
527         return tdesc;
528 }
529
530 int usecount (void)
531 {
532         /* never unload a config module */
533         return 1;
534 }
535
536 char *key ()
537 {
538         return ASTERISK_GPL_KEY;
539 }