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