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