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