Realtime improvements
[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 int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
171 {
172         odbc_obj *obj;
173         SQLHSTMT stmt;
174         char sql[256];
175         SQLLEN rowcount=0;
176         const char *newparam, *newval;
177         int res;
178         int x;
179         va_list aq;
180         
181         va_copy(aq, ap);
182         
183         if (!table)
184                 return -1;
185
186         obj = fetch_odbc_obj (database);
187         if (!obj)
188                 return -1;
189
190         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
191         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
192                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
193                 return -1;
194         }
195
196         newparam = va_arg(aq, const char *);
197         if (!newparam)  {
198                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
199                 return -1;
200         }
201         newval = va_arg(aq, const char *);
202         snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
203         while((newparam = va_arg(aq, const char *))) {
204                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
205                 newval = va_arg(aq, const char *);
206         }
207         va_end(aq);
208         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
209         
210         res = SQLPrepare(stmt, sql, SQL_NTS);
211         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
212                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
213                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
214                 return -1;
215         }
216         
217         /* Now bind the parameters */
218         x = 1;
219
220         while((newparam = va_arg(ap, const char *))) {
221                 newval = va_arg(ap, const char *);
222                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
223         }
224                 
225         SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(lookup), 0, (void *)lookup, 0, NULL);
226
227         res = SQLExecute(stmt);
228
229         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
230                 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
231                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
232                 return -1;
233         }
234
235         res = SQLRowCount(stmt, &rowcount);
236         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
237
238         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
239                 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
240                 return -1;
241         }
242
243         if (rowcount) 
244                 return 0;
245         return -1;
246 }
247
248 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)
249 {
250         struct ast_config *new;
251         struct ast_variable *cur_v, *new_v;
252         struct ast_category *cur_cat, *new_cat;
253         int res = 0;
254         odbc_obj *obj;
255         SQLINTEGER err=0, commented=0, cat_metric=0, var_metric=0, last_cat_metric=0;
256         SQLBIGINT id;
257         char sql[255] = "", filename[128], category[128], var_name[128], var_val[128];
258         SQLSMALLINT rowcount=0;
259         SQLHSTMT stmt;
260         char last[80] = "";
261         int cat_started = 0;
262         int var_started = 0;
263
264
265         if (!file || !strcmp (file, "res_config_odbc.conf"))
266                 return NULL;            // cant configure myself with myself !
267
268         obj = fetch_odbc_obj(database);
269         if (!obj)
270                 return NULL;
271
272         last[0] = '\0';
273
274         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
275
276         SQLBindCol (stmt, 1, SQL_C_ULONG, &id, sizeof (id), &err);
277         SQLBindCol (stmt, 2, SQL_C_ULONG, &cat_metric, sizeof (cat_metric), &err);
278         SQLBindCol (stmt, 3, SQL_C_ULONG, &var_metric, sizeof (var_metric), &err);
279         SQLBindCol (stmt, 4, SQL_C_ULONG, &commented, sizeof (commented), &err);
280         SQLBindCol (stmt, 5, SQL_C_CHAR, &filename, sizeof (filename), &err);
281         SQLBindCol (stmt, 6, SQL_C_CHAR, &category, sizeof (category), &err);
282         SQLBindCol (stmt, 7, SQL_C_CHAR, &var_name, sizeof (var_name), &err);
283         SQLBindCol (stmt, 8, SQL_C_CHAR, &var_val, sizeof (var_val), &err);
284
285         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);
286         res = SQLExecDirect (stmt, sql, SQL_NTS);
287
288         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
289                 ast_log (LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
290                 return NULL;
291         }
292
293         res = SQLNumResultCols (stmt, &rowcount);
294
295         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
296                 ast_log (LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
297                 return NULL;
298         }
299
300         if (new_config_s) {
301                 new = new_config_s;
302                 cat_started++;
303         } else {
304                 new = ast_new_config ();
305         }
306         
307         if (!new) {
308                 ast_log(LOG_WARNING, "Out of memory!\n");
309                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
310                 return NULL;
311         }
312
313         if (rowcount) {
314                 res = SQLFetch (stmt);
315                 cat_started = 0;
316
317                 cur_cat = *new_cat_p;
318                 cur_v = *new_v_p;
319
320                 if (cur_cat)
321                         cat_started = 1;
322                 if (cur_v)
323                         var_started = 1;
324
325                 while (res != SQL_NO_DATA) {
326                         if (!strcmp (var_name, "#include") && recur < MAX_INCLUDE_LEVEL) {
327
328                                 config_odbc(database, table, var_val, new, &cur_cat, &cur_v, recur + 1);
329                         } else {
330                                 if (strcmp (last, category) || last_cat_metric != cat_metric) {
331                                         strncpy(last, category, sizeof(last) - 1);
332                                         last_cat_metric = cat_metric;
333                                         new_cat = (struct ast_category *) ast_new_category (category);
334
335                                         if (!cat_started) {
336                                                 cat_started++;
337                                                 new->root = new_cat;
338                                                 cur_cat = new->root;
339                                         } else {
340                                                 cur_cat->next = new_cat;
341                                                 cur_cat = cur_cat->next;
342                                         }
343                                         var_started = 0;
344
345                                 }
346
347                                 new_v = ast_new_variable (var_name, var_val);
348
349                                 if (!var_started) {
350                                         var_started++;
351                                         cur_cat->root = new_v;
352                                         cur_v = cur_cat->root;
353                                 } else {
354                                         cur_v->next = new_v;
355                                         cur_v = cur_v->next;
356                                 }
357                         }
358
359                 // next row 
360                         res = SQLFetch (stmt);
361                 }
362
363                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
364         } else {
365                 ast_log (LOG_NOTICE, "found nothing\n");
366         }
367         return new;
368
369 }
370
371 int unload_module (void)
372 {
373         ast_cust_config_deregister (&reg1);
374         if (option_verbose)
375                 ast_verbose("res_config_odbc unloaded.\n");
376         STANDARD_HANGUP_LOCALUSERS;
377         return 0;
378 }
379
380 int load_module (void)
381 {
382         memset (&reg1, 0, sizeof (struct ast_config_reg));
383         strncpy(reg1.name, "odbc", sizeof(reg1.name) - 1);
384         reg1.static_func = config_odbc;
385         reg1.realtime_func = realtime_odbc;
386         reg1.update_func = update_odbc;
387         ast_cust_config_register (&reg1);
388         if (option_verbose)
389                 ast_verbose("res_config_odbc loaded.\n");
390         return 0;
391 }
392
393 char *description (void)
394 {
395         return tdesc;
396 }
397
398 int usecount (void)
399 {
400         /* never unload a config module */
401         return 1;
402 }
403
404 char *key ()
405 {
406         return ASTERISK_GPL_KEY;
407 }