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