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