Minor 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 void parseop(char *newp, int newplen, char **opo, const char *newparam)
35 {
36         char *op;
37         strncpy(newp, newparam, newplen - 1);
38         op = strchr(newp, ' ');
39         if (op) {
40                 *op = '\0';
41                 op++;
42         } else
43                 op = "=";
44         *opo = op;
45 }
46
47 static struct ast_variable *realtime_odbc(const char *database, const char *table, va_list ap)
48 {
49         odbc_obj *obj;
50         SQLHSTMT stmt;
51         char sql[1024];
52         char coltitle[256];
53         char rowdata[2048];
54         char newp[256];
55         char *op;
56         const char *newparam, *newval;
57         char *stringp;
58         char *chunk;
59         SQLSMALLINT collen;
60         int res;
61         int x;
62         struct ast_variable *var=NULL, *prev=NULL;
63         SQLLEN rowcount=0;
64         SQLULEN colsize;
65         SQLSMALLINT colcount=0;
66         SQLSMALLINT datatype;
67         SQLSMALLINT decimaldigits;
68         SQLSMALLINT nullable;
69         va_list aq;
70         
71         va_copy(aq, ap);
72         
73         
74         if (!table)
75                 return NULL;
76
77         obj = fetch_odbc_obj(database);
78         if (!obj)
79                 return NULL;
80
81         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
82         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
83                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
84                 return NULL;
85         }
86
87         newparam = va_arg(aq, const char *);
88         if (!newparam)  {
89                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
90                 return NULL;
91         }
92         newval = va_arg(aq, const char *);
93         parseop(newp, sizeof(newp), &op, newparam);
94         snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s %s ?", table, newp, op);
95         while((newparam = va_arg(aq, const char *))) {
96                 parseop(newp, sizeof(newp), &op, newparam);
97                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s %s ?", newparam, op);
98                 newval = va_arg(aq, const char *);
99         }
100         va_end(aq);
101         
102         res = SQLPrepare(stmt, sql, SQL_NTS);
103         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
104                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
105                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
106                 return NULL;
107         }
108         
109         /* Now bind the parameters */
110         x = 1;
111
112         while((newparam = va_arg(ap, const char *))) {
113                 newval = va_arg(ap, const char *);
114                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
115         }
116                 
117         res = SQLExecute(stmt);
118
119         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
120                 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
121                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
122                 return NULL;
123         }
124
125         res = SQLRowCount(stmt, &rowcount);
126         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
127                 ast_log(LOG_WARNING, "SQL Row Count 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         if (rowcount) {
140                 res = SQLFetch(stmt);
141                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
142                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
143                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
144                         return NULL;
145                 }
146                 for (x=0;x<colcount;x++) {
147                         rowdata[0] = '\0';
148                         collen = sizeof(coltitle);
149                         res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
150                                                 &datatype, &colsize, &decimaldigits, &nullable);
151                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
152                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
153                                 if (var)
154                                         ast_destroy_realtime(var);
155                                 return NULL;
156                         }
157                         res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
158                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
159                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
160                                 if (var)
161                                         ast_destroy_realtime(var);
162                                 return NULL;
163                         }
164                         stringp = rowdata;
165                         while(stringp) {
166                                 chunk = strsep(&stringp, ";");
167                                 if (chunk && !ast_strlen_zero(ast_strip(chunk))) {
168                                         if (prev) {
169                                                 prev->next = ast_new_variable(coltitle, chunk);
170                                                 if (prev->next)
171                                                         prev = prev->next;
172                                         } else 
173                                                 prev = var = ast_new_variable(coltitle, chunk);
174                                         
175                                 }
176                         }
177                 }
178         }
179
180
181         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
182         return var;
183 }
184
185 static int update_odbc(const char *database, const char *table, const char *keyfield, const char *lookup, va_list ap)
186 {
187         odbc_obj *obj;
188         SQLHSTMT stmt;
189         char sql[256];
190         SQLLEN rowcount=0;
191         const char *newparam, *newval;
192         int res;
193         int x;
194         va_list aq;
195         
196         va_copy(aq, ap);
197         
198         if (!table)
199                 return -1;
200
201         obj = fetch_odbc_obj (database);
202         if (!obj)
203                 return -1;
204
205         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
206         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
207                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
208                 return -1;
209         }
210
211         newparam = va_arg(aq, const char *);
212         if (!newparam)  {
213                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
214                 return -1;
215         }
216         newval = va_arg(aq, const char *);
217         snprintf(sql, sizeof(sql), "UPDATE %s SET %s=?", table, newparam);
218         while((newparam = va_arg(aq, const char *))) {
219                 snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ", %s=?", newparam);
220                 newval = va_arg(aq, const char *);
221         }
222         va_end(aq);
223         snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " WHERE %s=?", keyfield);
224         
225         res = SQLPrepare(stmt, sql, SQL_NTS);
226         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
227                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
228                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
229                 return -1;
230         }
231         
232         /* Now bind the parameters */
233         x = 1;
234
235         while((newparam = va_arg(ap, const char *))) {
236                 newval = va_arg(ap, const char *);
237                 SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL);
238         }
239                 
240         SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(lookup), 0, (void *)lookup, 0, NULL);
241
242         res = SQLExecute(stmt);
243
244         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
245                 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
246                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
247                 return -1;
248         }
249
250         res = SQLRowCount(stmt, &rowcount);
251         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
252
253         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
254                 ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
255                 return -1;
256         }
257
258         if (rowcount) 
259                 return 0;
260         return -1;
261 }
262
263 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)
264 {
265         struct ast_config *new;
266         struct ast_variable *cur_v, *new_v;
267         struct ast_category *cur_cat, *new_cat;
268         int res = 0;
269         odbc_obj *obj;
270         SQLINTEGER err=0, commented=0, cat_metric=0, var_metric=0, last_cat_metric=0;
271         SQLBIGINT id;
272         char sql[255] = "", filename[128], category[128], var_name[128], var_val[128];
273         SQLSMALLINT rowcount=0;
274         SQLHSTMT stmt;
275         char last[80] = "";
276         int cat_started = 0;
277         int var_started = 0;
278
279
280         if (!file || !strcmp (file, "res_config_odbc.conf"))
281                 return NULL;            // cant configure myself with myself !
282
283         obj = fetch_odbc_obj(database);
284         if (!obj)
285                 return NULL;
286
287         last[0] = '\0';
288
289         res = SQLAllocHandle (SQL_HANDLE_STMT, obj->con, &stmt);
290
291         SQLBindCol (stmt, 1, SQL_C_ULONG, &id, sizeof (id), &err);
292         SQLBindCol (stmt, 2, SQL_C_ULONG, &cat_metric, sizeof (cat_metric), &err);
293         SQLBindCol (stmt, 3, SQL_C_ULONG, &var_metric, sizeof (var_metric), &err);
294         SQLBindCol (stmt, 4, SQL_C_ULONG, &commented, sizeof (commented), &err);
295         SQLBindCol (stmt, 5, SQL_C_CHAR, &filename, sizeof (filename), &err);
296         SQLBindCol (stmt, 6, SQL_C_CHAR, &category, sizeof (category), &err);
297         SQLBindCol (stmt, 7, SQL_C_CHAR, &var_name, sizeof (var_name), &err);
298         SQLBindCol (stmt, 8, SQL_C_CHAR, &var_val, sizeof (var_val), &err);
299
300         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);
301         res = SQLExecDirect (stmt, sql, SQL_NTS);
302
303         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
304                 ast_log (LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
305                 return NULL;
306         }
307
308         res = SQLNumResultCols (stmt, &rowcount);
309
310         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
311                 ast_log (LOG_WARNING, "SQL select error!\n[%s]\n\n", sql);
312                 return NULL;
313         }
314
315         if (new_config_s) {
316                 new = new_config_s;
317                 cat_started++;
318         } else {
319                 new = ast_new_config ();
320         }
321         
322         if (!new) {
323                 ast_log(LOG_WARNING, "Out of memory!\n");
324                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
325                 return NULL;
326         }
327
328         if (rowcount) {
329                 res = SQLFetch (stmt);
330                 cat_started = 0;
331
332                 cur_cat = *new_cat_p;
333                 cur_v = *new_v_p;
334
335                 if (cur_cat)
336                         cat_started = 1;
337                 if (cur_v)
338                         var_started = 1;
339
340                 while (res != SQL_NO_DATA) {
341                         if (!strcmp (var_name, "#include") && recur < MAX_INCLUDE_LEVEL) {
342
343                                 config_odbc(database, table, var_val, new, &cur_cat, &cur_v, recur + 1);
344                         } else {
345                                 if (strcmp (last, category) || last_cat_metric != cat_metric) {
346                                         strncpy(last, category, sizeof(last) - 1);
347                                         last_cat_metric = cat_metric;
348                                         new_cat = (struct ast_category *) ast_new_category (category);
349
350                                         if (!cat_started) {
351                                                 cat_started++;
352                                                 new->root = new_cat;
353                                                 cur_cat = new->root;
354                                         } else {
355                                                 cur_cat->next = new_cat;
356                                                 cur_cat = cur_cat->next;
357                                         }
358                                         var_started = 0;
359
360                                 }
361
362                                 new_v = ast_new_variable (var_name, var_val);
363
364                                 if (!var_started) {
365                                         var_started++;
366                                         cur_cat->root = new_v;
367                                         cur_v = cur_cat->root;
368                                 } else {
369                                         cur_v->next = new_v;
370                                         cur_v = cur_v->next;
371                                 }
372                         }
373
374                 // next row 
375                         res = SQLFetch (stmt);
376                 }
377
378                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
379         } else {
380                 ast_log (LOG_NOTICE, "found nothing\n");
381         }
382         return new;
383
384 }
385
386 int unload_module (void)
387 {
388         ast_cust_config_deregister (&reg1);
389         if (option_verbose)
390                 ast_verbose("res_config_odbc unloaded.\n");
391         STANDARD_HANGUP_LOCALUSERS;
392         return 0;
393 }
394
395 int load_module (void)
396 {
397         memset (&reg1, 0, sizeof (struct ast_config_reg));
398         strncpy(reg1.name, "odbc", sizeof(reg1.name) - 1);
399         reg1.static_func = config_odbc;
400         reg1.realtime_func = realtime_odbc;
401         reg1.update_func = update_odbc;
402         ast_cust_config_register (&reg1);
403         if (option_verbose)
404                 ast_verbose("res_config_odbc loaded.\n");
405         return 0;
406 }
407
408 char *description (void)
409 {
410         return tdesc;
411 }
412
413 int usecount (void)
414 {
415         /* never unload a config module */
416         return 1;
417 }
418
419 char *key ()
420 {
421         return ASTERISK_GPL_KEY;
422 }