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