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