resolve (valid) compiler warning about variable that could be used before being initi...
[asterisk/asterisk.git] / res / res_config_curl.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2008, Digium, Inc.
5  *
6  * Tilghman Lesher <res_config_curl_v1@the-tilghman.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief curl plugin for portable configuration engine
22  *
23  * \author Tilghman Lesher <res_config_curl_v1@the-tilghman.com>
24  *
25  * \extref Depends on the CURL library  - http://curl.haxx.se/
26  * 
27  */
28
29 /*** MODULEINFO
30         <depend>curl</depend>
31  ***/
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include <curl/curl.h>
38
39 #include "asterisk/file.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/config.h"
43 #include "asterisk/module.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/utils.h"
46
47 /*!
48  * \brief Execute a curl query and return ast_variable list
49  * \param url The base URL from which to retrieve data
50  * \param unused Not currently used
51  * \param ap list containing one or more field/operator/value set.
52  *
53  * \retval var on success
54  * \retval NULL on failure
55 */
56 static struct ast_variable *realtime_curl(const char *url, const char *unused, va_list ap)
57 {
58         struct ast_str *query;
59         char buf1[200], buf2[200];
60         const char *newparam, *newval;
61         char *stringp, *pair, *key;
62         int i;
63         struct ast_variable *var=NULL, *prev=NULL;
64         const int EncodeSpecialChars = 1;
65         char *buffer;
66
67         if (!ast_custom_function_find("CURL")) {
68                 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
69                 return NULL;
70         }
71
72         if (!(query = ast_str_create(1000)))
73                 return NULL;
74
75         if (!(buffer = ast_malloc(64000))) {
76                 ast_free(query);
77                 return NULL;
78         }
79
80         ast_str_set(&query, 0, "${CURL(%s,", url);
81
82         for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
83                 newval = va_arg(ap, const char *);
84                 ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
85                 ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
86                 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
87         }
88         va_end(ap);
89
90         ast_str_append(&query, 0, ")}");
91         pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
92
93         /* Remove any trailing newline characters */
94         if ((stringp = strchr(buffer, '\r')) || (stringp = strchr(buffer, '\n')))
95                 *stringp = '\0';
96
97         stringp = buffer;
98         while ((pair = strsep(&stringp, "&"))) {
99                 key = strsep(&pair, "=");
100                 ast_uri_decode(key);
101                 if (pair)
102                         ast_uri_decode(pair);
103
104                 if (!ast_strlen_zero(key)) {
105                         if (prev) {
106                                 prev->next = ast_variable_new(key, S_OR(pair, ""), "");
107                                 if (prev->next)
108                                         prev = prev->next;
109                         } else 
110                                 prev = var = ast_variable_new(key, S_OR(pair, ""), "");
111                 }
112         }
113
114         ast_free(buffer);
115         ast_free(query);
116         return var;
117 }
118
119 /*!
120  * \brief Excute an Select query and return ast_config list
121  * \param url
122  * \param unused
123  * \param ap list containing one or more field/operator/value set.
124  *
125  * \retval struct ast_config pointer on success
126  * \retval NULL on failure
127 */
128 static struct ast_config *realtime_multi_curl(const char *url, const char *unused, va_list ap)
129 {
130         struct ast_str *query;
131         char buf1[200], buf2[200];
132         const char *newparam, *newval;
133         char *stringp, *line, *pair, *key, *initfield = NULL;
134         int i, EncodeSpecialChars = 1;
135         struct ast_variable *var=NULL;
136         struct ast_config *cfg=NULL;
137         struct ast_category *cat=NULL;
138         char *buffer;
139
140         if (!ast_custom_function_find("CURL")) {
141                 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
142                 return NULL;
143         }
144
145         if (!(query = ast_str_create(1000)))
146                 return NULL;
147
148         if (!(buffer = ast_malloc(256000))) {
149                 ast_free(query);
150                 return NULL;
151         }
152
153         ast_str_set(&query, 0, "${CURL(%s/multi,", url);
154
155         for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
156                 newval = va_arg(ap, const char *);
157                 if (i == 0)
158                         initfield = ast_strdupa(newparam);
159                 ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
160                 ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
161                 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
162         }
163         va_end(ap);
164
165         ast_str_append(&query, 0, ")}");
166
167         /* Do the CURL query */
168         pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
169
170         if (!(cfg = ast_config_new()))
171                 goto exit_multi;
172
173         /* Line oriented output */
174         stringp = buffer;
175         while ((line = strsep(&stringp, "\r\n"))) {
176                 if (ast_strlen_zero(line))
177                         continue;
178
179                 if (!(cat = ast_category_new("", "", 99999)))
180                         continue;
181
182                 while ((pair = strsep(&line, "&"))) {
183                         key = strsep(&pair, "=");
184                         ast_uri_decode(key);
185                         if (pair)
186                                 ast_uri_decode(pair);
187
188                         if (!strcasecmp(key, initfield) && pair)
189                                 ast_category_rename(cat, pair);
190
191                         if (!ast_strlen_zero(key)) {
192                                 var = ast_variable_new(key, S_OR(pair, ""), "");
193                                 ast_variable_append(cat, var);
194                         }
195                 }
196                 ast_category_append(cfg, cat);
197         }
198
199 exit_multi:
200         ast_free(buffer);
201         ast_free(query);
202         return cfg;
203 }
204
205 /*!
206  * \brief Execute an UPDATE query
207  * \param url
208  * \param unused
209  * \param keyfield where clause field
210  * \param lookup value of field for where clause
211  * \param ap list containing one or more field/value set(s).
212  *
213  * Update a database table, prepare the sql statement using keyfield and lookup
214  * control the number of records to change. All values to be changed are stored in ap list.
215  * Sub-in the values to the prepared statement and execute it.
216  *
217  * \retval number of rows affected
218  * \retval -1 on failure
219 */
220 static int update_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
221 {
222         struct ast_str *query;
223         char buf1[200], buf2[200];
224         const char *newparam, *newval;
225         char *stringp;
226         int i, rowcount = -1;
227         const int EncodeSpecialChars = 1;
228         char *buffer;
229
230         if (!ast_custom_function_find("CURL")) {
231                 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
232                 return -1;
233         }
234
235         if (!(query = ast_str_create(1000)))
236                 return -1;
237
238         if (!(buffer = ast_malloc(100))) {
239                 ast_free(query);
240                 return -1;
241         }
242
243         ast_uri_encode(keyfield, buf1, sizeof(buf1), EncodeSpecialChars);
244         ast_uri_encode(lookup, buf2, sizeof(buf2), EncodeSpecialChars);
245         ast_str_set(&query, 0, "${CURL(%s/update?%s=%s,", url, buf1, buf2);
246
247         for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
248                 newval = va_arg(ap, const char *);
249                 ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
250                 ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
251                 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
252         }
253         va_end(ap);
254
255         ast_str_append(&query, 0, ")}");
256         pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
257
258         /* Line oriented output */
259         stringp = buffer;
260         while (*stringp <= ' ')
261                 stringp++;
262         sscanf(stringp, "%d", &rowcount);
263
264         ast_free(buffer);
265         ast_free(query);
266
267         if (rowcount >= 0)
268                 return (int)rowcount;
269
270         return -1;
271 }
272
273 /*!
274  * \brief Execute an INSERT query
275  * \param database
276  * \param table
277  * \param ap list containing one or more field/value set(s)
278  *
279  * Insert a new record into database table, prepare the sql statement.
280  * All values to be changed are stored in ap list.
281  * Sub-in the values to the prepared statement and execute it.
282  *
283  * \retval number of rows affected
284  * \retval -1 on failure
285 */
286 static int store_curl(const char *url, const char *unused, va_list ap)
287 {
288         struct ast_str *query;
289         char buf1[200], buf2[200];
290         const char *newparam, *newval;
291         char *stringp;
292         int i, rowcount = -1;
293         const int EncodeSpecialChars = 1;
294         char *buffer;
295
296         if (!ast_custom_function_find("CURL")) {
297                 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
298                 return -1;
299         }
300
301         if (!(query = ast_str_create(1000)))
302                 return -1;
303
304         if (!(buffer = ast_malloc(100))) {
305                 ast_free(query);
306                 return -1;
307         }
308
309         ast_str_set(&query, 0, "${CURL(%s/store,", url);
310
311         for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
312                 newval = va_arg(ap, const char *);
313                 ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
314                 ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
315                 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
316         }
317         va_end(ap);
318
319         ast_str_append(&query, 0, ")}");
320         pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
321
322         stringp = buffer;
323         while (*stringp <= ' ')
324                 stringp++;
325         sscanf(stringp, "%d", &rowcount);
326
327         ast_free(buffer);
328         ast_free(query);
329
330         if (rowcount >= 0)
331                 return (int)rowcount;
332
333         return -1;
334 }
335
336 /*!
337  * \brief Execute an DELETE query
338  * \param url
339  * \param unused
340  * \param keyfield where clause field
341  * \param lookup value of field for where clause
342  * \param ap list containing one or more field/value set(s)
343  *
344  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
345  * control the number of records to change. Additional params to match rows are stored in ap list.
346  * Sub-in the values to the prepared statement and execute it.
347  *
348  * \retval number of rows affected
349  * \retval -1 on failure
350 */
351 static int destroy_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
352 {
353         struct ast_str *query;
354         char buf1[200], buf2[200];
355         const char *newparam, *newval;
356         char *stringp;
357         int i, rowcount = -1;
358         const int EncodeSpecialChars = 1;
359         char *buffer;
360
361         if (!ast_custom_function_find("CURL")) {
362                 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
363                 return -1;
364         }
365
366         if (!(query = ast_str_create(1000)))
367                 return -1;
368
369         if (!(buffer = ast_malloc(100))) {
370                 ast_free(query);
371                 return -1;
372         }
373
374         ast_uri_encode(keyfield, buf1, sizeof(buf1), EncodeSpecialChars);
375         ast_uri_encode(lookup, buf2, sizeof(buf2), EncodeSpecialChars);
376         ast_str_set(&query, 0, "${CURL(%s/destroy,%s=%s&", url, buf1, buf2);
377
378         for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
379                 newval = va_arg(ap, const char *);
380                 ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
381                 ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
382                 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
383         }
384         va_end(ap);
385
386         ast_str_append(&query, 0, ")}");
387         pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
388
389         /* Line oriented output */
390         stringp = buffer;
391         while (*stringp <= ' ')
392                 stringp++;
393         sscanf(stringp, "%d", &rowcount);
394
395         ast_free(buffer);
396         ast_free(query);
397
398         if (rowcount >= 0)
399                 return (int)rowcount;
400
401         return -1;
402 }
403
404
405 static struct ast_config *config_curl(const char *url, const char *unused, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl)
406 {
407         struct ast_str *query;
408         char buf1[200];
409         char *stringp, *line, *pair, *key;
410         int EncodeSpecialChars = 1, last_cat_metric = -1, cat_metric = -1;
411         struct ast_category *cat=NULL;
412         char *buffer, *cur_cat = "";
413         char *category = "", *var_name = "", *var_val = "";
414         struct ast_flags loader_flags = { 0 };
415
416         if (!ast_custom_function_find("CURL")) {
417                 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
418                 return NULL;
419         }
420
421         if (!(query = ast_str_create(1000)))
422                 return NULL;
423
424         if (!(buffer = ast_malloc(256000))) {
425                 ast_free(query);
426                 return NULL;
427         }
428
429         ast_uri_encode(file, buf1, sizeof(buf1), EncodeSpecialChars);
430         ast_str_set(&query, 0, "${CURL(%s/static?file=%s)}", url, buf1);
431
432         /* Do the CURL query */
433         pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
434
435         /* Line oriented output */
436         stringp = buffer;
437         cat = ast_config_get_current_category(cfg);
438
439         while ((line = strsep(&stringp, "\r\n"))) {
440                 if (ast_strlen_zero(line))
441                         continue;
442
443                 while ((pair = strsep(&line, "&"))) {
444                         key = strsep(&pair, "=");
445                         ast_uri_decode(key);
446                         if (pair)
447                                 ast_uri_decode(pair);
448
449                         if (!strcasecmp(key, "category"))
450                                 category = S_OR(pair, "");
451                         else if (!strcasecmp(key, "var_name"))
452                                 var_name = S_OR(pair, "");
453                         else if (!strcasecmp(key, "var_val"))
454                                 var_val = S_OR(pair, "");
455                         else if (!strcasecmp(key, "cat_metric"))
456                                 cat_metric = pair ? atoi(pair) : 0;
457                 }
458
459                 if (!strcmp(var_name, "#include")) {
460                         if (!ast_config_internal_load(var_val, cfg, loader_flags, ""))
461                                 return NULL;
462                 }
463
464                 if (strcmp(category, cur_cat) || last_cat_metric != cat_metric) {
465                         if (!(cat = ast_category_new(category, "", 99999)))
466                                 break;
467                         cur_cat = category;
468                         last_cat_metric = cat_metric;
469                         ast_category_append(cfg, cat);
470                 }
471                 ast_variable_append(cat, ast_variable_new(var_name, var_val, ""));
472         }
473
474         ast_free(buffer);
475         ast_free(query);
476         return cfg;
477 }
478
479 static struct ast_config_engine curl_engine = {
480         .name = "curl",
481         .load_func = config_curl,
482         .realtime_func = realtime_curl,
483         .realtime_multi_func = realtime_multi_curl,
484         .store_func = store_curl,
485         .destroy_func = destroy_curl,
486         .update_func = update_curl
487 };
488
489 static int unload_module (void)
490 {
491         ast_config_engine_deregister(&curl_engine);
492         ast_verb(1, "res_config_curl unloaded.\n");
493         return 0;
494 }
495
496 static int load_module (void)
497 {
498         ast_config_engine_register(&curl_engine);
499         ast_verb(1, "res_config_curl loaded.\n");
500         return 0;
501 }
502
503 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Curl configuration");