Thu Apr 28 2011 17:15:24

Asterisk developer's documentation


res_config_curl.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2008, Digium, Inc.
00005  *
00006  * Tilghman Lesher <res_config_curl_v1@the-tilghman.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief curl plugin for portable configuration engine
00022  *
00023  * \author Tilghman Lesher <res_config_curl_v1@the-tilghman.com>
00024  *
00025  * \extref Depends on the CURL library  - http://curl.haxx.se/
00026  * 
00027  */
00028 
00029 /*** MODULEINFO
00030    <depend>curl</depend>
00031  ***/
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 280556 $")
00036 
00037 #include <curl/curl.h>
00038 
00039 #include "asterisk/file.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/module.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/utils.h"
00046 
00047 /*!
00048  * \brief Execute a curl query and return ast_variable list
00049  * \param url The base URL from which to retrieve data
00050  * \param unused Not currently used
00051  * \param ap list containing one or more field/operator/value set.
00052  *
00053  * \retval var on success
00054  * \retval NULL on failure
00055 */
00056 static struct ast_variable *realtime_curl(const char *url, const char *unused, va_list ap)
00057 {
00058    struct ast_str *query;
00059    char buf1[200], buf2[200];
00060    const char *newparam, *newval;
00061    char *stringp, *pair, *key;
00062    int i;
00063    struct ast_variable *var=NULL, *prev=NULL;
00064    const int EncodeSpecialChars = 1, bufsize = 64000;
00065    char *buffer;
00066 
00067    if (!ast_custom_function_find("CURL")) {
00068       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00069       return NULL;
00070    }
00071 
00072    if (!(query = ast_str_create(1000)))
00073       return NULL;
00074 
00075    if (!(buffer = ast_malloc(bufsize))) {
00076       ast_free(query);
00077       return NULL;
00078    }
00079 
00080    ast_str_set(&query, 0, "${CURL(%s/single,", url);
00081 
00082    for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00083       newval = va_arg(ap, const char *);
00084       ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
00085       ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
00086       ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00087    }
00088    va_end(ap);
00089 
00090    ast_str_append(&query, 0, ")}");
00091    pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize - 1);
00092 
00093    /* Remove any trailing newline characters */
00094    if ((stringp = strchr(buffer, '\r')) || (stringp = strchr(buffer, '\n')))
00095       *stringp = '\0';
00096 
00097    stringp = buffer;
00098    while ((pair = strsep(&stringp, "&"))) {
00099       key = strsep(&pair, "=");
00100       ast_uri_decode(key);
00101       if (pair)
00102          ast_uri_decode(pair);
00103 
00104       if (!ast_strlen_zero(key)) {
00105          if (prev) {
00106             prev->next = ast_variable_new(key, S_OR(pair, ""), "");
00107             if (prev->next)
00108                prev = prev->next;
00109          } else 
00110             prev = var = ast_variable_new(key, S_OR(pair, ""), "");
00111       }
00112    }
00113 
00114    ast_free(buffer);
00115    ast_free(query);
00116    return var;
00117 }
00118 
00119 /*!
00120  * \brief Excute an Select query and return ast_config list
00121  * \param url
00122  * \param unused
00123  * \param ap list containing one or more field/operator/value set.
00124  *
00125  * \retval struct ast_config pointer on success
00126  * \retval NULL on failure
00127 */
00128 static struct ast_config *realtime_multi_curl(const char *url, const char *unused, va_list ap)
00129 {
00130    struct ast_str *query;
00131    char buf1[200], buf2[200];
00132    const char *newparam, *newval;
00133    char *stringp, *line, *pair, *key, *initfield = NULL;
00134    int i;
00135    const int EncodeSpecialChars = 1, bufsize = 256000;
00136    struct ast_variable *var=NULL;
00137    struct ast_config *cfg=NULL;
00138    struct ast_category *cat=NULL;
00139    char *buffer;
00140 
00141    if (!ast_custom_function_find("CURL")) {
00142       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00143       return NULL;
00144    }
00145 
00146    if (!(query = ast_str_create(1000)))
00147       return NULL;
00148 
00149    if (!(buffer = ast_malloc(bufsize))) {
00150       ast_free(query);
00151       return NULL;
00152    }
00153 
00154    ast_str_set(&query, 0, "${CURL(%s/multi,", url);
00155 
00156    for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00157       newval = va_arg(ap, const char *);
00158       if (i == 0) {
00159          char *op;
00160          initfield = ast_strdupa(newparam);
00161          if ((op = strchr(initfield, ' ')))
00162             *op = '\0';
00163       }
00164       ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
00165       ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
00166       ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00167    }
00168    va_end(ap);
00169 
00170    ast_str_append(&query, 0, ")}");
00171 
00172    /* Do the CURL query */
00173    pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize - 1);
00174 
00175    if (!(cfg = ast_config_new()))
00176       goto exit_multi;
00177 
00178    /* Line oriented output */
00179    stringp = buffer;
00180    while ((line = strsep(&stringp, "\r\n"))) {
00181       if (ast_strlen_zero(line))
00182          continue;
00183 
00184       if (!(cat = ast_category_new("", "", 99999)))
00185          continue;
00186 
00187       while ((pair = strsep(&line, "&"))) {
00188          key = strsep(&pair, "=");
00189          ast_uri_decode(key);
00190          if (pair)
00191             ast_uri_decode(pair);
00192 
00193          if (!strcasecmp(key, initfield) && pair)
00194             ast_category_rename(cat, pair);
00195 
00196          if (!ast_strlen_zero(key)) {
00197             var = ast_variable_new(key, S_OR(pair, ""), "");
00198             ast_variable_append(cat, var);
00199          }
00200       }
00201       ast_category_append(cfg, cat);
00202    }
00203 
00204 exit_multi:
00205    ast_free(buffer);
00206    ast_free(query);
00207    return cfg;
00208 }
00209 
00210 /*!
00211  * \brief Execute an UPDATE query
00212  * \param url
00213  * \param unused
00214  * \param keyfield where clause field
00215  * \param lookup value of field for where clause
00216  * \param ap list containing one or more field/value set(s).
00217  *
00218  * Update a database table, prepare the sql statement using keyfield and lookup
00219  * control the number of records to change. All values to be changed are stored in ap list.
00220  * Sub-in the values to the prepared statement and execute it.
00221  *
00222  * \retval number of rows affected
00223  * \retval -1 on failure
00224 */
00225 static int update_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
00226 {
00227    struct ast_str *query;
00228    char buf1[200], buf2[200];
00229    const char *newparam, *newval;
00230    char *stringp;
00231    int i, rowcount = -1;
00232    const int EncodeSpecialChars = 1, bufsize = 100;
00233    char *buffer;
00234 
00235    if (!ast_custom_function_find("CURL")) {
00236       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00237       return -1;
00238    }
00239 
00240    if (!(query = ast_str_create(1000)))
00241       return -1;
00242 
00243    if (!(buffer = ast_malloc(bufsize))) {
00244       ast_free(query);
00245       return -1;
00246    }
00247 
00248    ast_uri_encode(keyfield, buf1, sizeof(buf1), EncodeSpecialChars);
00249    ast_uri_encode(lookup, buf2, sizeof(buf2), EncodeSpecialChars);
00250    ast_str_set(&query, 0, "${CURL(%s/update?%s=%s,", url, buf1, buf2);
00251 
00252    for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00253       newval = va_arg(ap, const char *);
00254       ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
00255       ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
00256       ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00257    }
00258    va_end(ap);
00259 
00260    ast_str_append(&query, 0, ")}");
00261    pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize - 1);
00262 
00263    /* Line oriented output */
00264    stringp = buffer;
00265    while (*stringp <= ' ')
00266       stringp++;
00267    sscanf(stringp, "%30d", &rowcount);
00268 
00269    ast_free(buffer);
00270    ast_free(query);
00271 
00272    if (rowcount >= 0)
00273       return (int)rowcount;
00274 
00275    return -1;
00276 }
00277 
00278 static int update2_curl(const char *url, const char *unused, va_list ap)
00279 {
00280    struct ast_str *query;
00281    char buf1[200], buf2[200];
00282    const char *newparam, *newval;
00283    char *stringp;
00284    int rowcount = -1, lookup = 1, first = 1;
00285    const int EncodeSpecialChars = 1, bufsize = 100;
00286    char *buffer;
00287 
00288    if (!ast_custom_function_find("CURL")) {
00289       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00290       return -1;
00291    }
00292 
00293    if (!(query = ast_str_create(1000)))
00294       return -1;
00295 
00296    if (!(buffer = ast_malloc(bufsize))) {
00297       ast_free(query);
00298       return -1;
00299    }
00300 
00301    ast_str_set(&query, 0, "${CURL(%s/update?", url);
00302 
00303    for (;;) {
00304       if ((newparam = va_arg(ap, const char *)) == SENTINEL) {
00305          if (lookup) {
00306             lookup = 0;
00307             ast_str_append(&query, 0, ",");
00308             /* Back to the first parameter; we don't need a starting '&' */
00309             first = 1;
00310             continue;
00311          } else {
00312             break;
00313          }
00314       }
00315       newval = va_arg(ap, const char *);
00316       ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
00317       ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
00318       ast_str_append(&query, 0, "%s%s=%s", first ? "" : "&", buf1, buf2);
00319    }
00320    va_end(ap);
00321 
00322    ast_str_append(&query, 0, ")}");
00323    /* TODO: Make proxies work */
00324    pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize - 1);
00325 
00326    /* Line oriented output */
00327    stringp = buffer;
00328    while (*stringp <= ' ')
00329       stringp++;
00330    sscanf(stringp, "%30d", &rowcount);
00331 
00332    ast_free(buffer);
00333    ast_free(query);
00334 
00335    if (rowcount >= 0)
00336       return (int)rowcount;
00337 
00338    return -1;
00339 }
00340 
00341 /*!
00342  * \brief Execute an INSERT query
00343  * \param url
00344  * \param unused
00345  * \param ap list containing one or more field/value set(s)
00346  *
00347  * Insert a new record into database table, prepare the sql statement.
00348  * All values to be changed are stored in ap list.
00349  * Sub-in the values to the prepared statement and execute it.
00350  *
00351  * \retval number of rows affected
00352  * \retval -1 on failure
00353 */
00354 static int store_curl(const char *url, const char *unused, va_list ap)
00355 {
00356    struct ast_str *query;
00357    char buf1[200], buf2[200];
00358    const char *newparam, *newval;
00359    char *stringp;
00360    int i, rowcount = -1;
00361    const int EncodeSpecialChars = 1, bufsize = 100;
00362    char *buffer;
00363 
00364    if (!ast_custom_function_find("CURL")) {
00365       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00366       return -1;
00367    }
00368 
00369    if (!(query = ast_str_create(1000)))
00370       return -1;
00371 
00372    if (!(buffer = ast_malloc(bufsize))) {
00373       ast_free(query);
00374       return -1;
00375    }
00376 
00377    ast_str_set(&query, 0, "${CURL(%s/store,", url);
00378 
00379    for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00380       newval = va_arg(ap, const char *);
00381       ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
00382       ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
00383       ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00384    }
00385    va_end(ap);
00386 
00387    ast_str_append(&query, 0, ")}");
00388    pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize - 1);
00389 
00390    stringp = buffer;
00391    while (*stringp <= ' ')
00392       stringp++;
00393    sscanf(stringp, "%30d", &rowcount);
00394 
00395    ast_free(buffer);
00396    ast_free(query);
00397 
00398    if (rowcount >= 0)
00399       return (int)rowcount;
00400 
00401    return -1;
00402 }
00403 
00404 /*!
00405  * \brief Execute an DELETE query
00406  * \param url
00407  * \param unused
00408  * \param keyfield where clause field
00409  * \param lookup value of field for where clause
00410  * \param ap list containing one or more field/value set(s)
00411  *
00412  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
00413  * control the number of records to change. Additional params to match rows are stored in ap list.
00414  * Sub-in the values to the prepared statement and execute it.
00415  *
00416  * \retval number of rows affected
00417  * \retval -1 on failure
00418 */
00419 static int destroy_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
00420 {
00421    struct ast_str *query;
00422    char buf1[200], buf2[200];
00423    const char *newparam, *newval;
00424    char *stringp;
00425    int i, rowcount = -1;
00426    const int EncodeSpecialChars = 1, bufsize = 100;
00427    char *buffer;
00428 
00429    if (!ast_custom_function_find("CURL")) {
00430       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00431       return -1;
00432    }
00433 
00434    if (!(query = ast_str_create(1000)))
00435       return -1;
00436 
00437    if (!(buffer = ast_malloc(bufsize))) {
00438       ast_free(query);
00439       return -1;
00440    }
00441 
00442    ast_uri_encode(keyfield, buf1, sizeof(buf1), EncodeSpecialChars);
00443    ast_uri_encode(lookup, buf2, sizeof(buf2), EncodeSpecialChars);
00444    ast_str_set(&query, 0, "${CURL(%s/destroy,%s=%s&", url, buf1, buf2);
00445 
00446    for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00447       newval = va_arg(ap, const char *);
00448       ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
00449       ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
00450       ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00451    }
00452    va_end(ap);
00453 
00454    ast_str_append(&query, 0, ")}");
00455    pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize - 1);
00456 
00457    /* Line oriented output */
00458    stringp = buffer;
00459    while (*stringp <= ' ')
00460       stringp++;
00461    sscanf(stringp, "%30d", &rowcount);
00462 
00463    ast_free(buffer);
00464    ast_free(query);
00465 
00466    if (rowcount >= 0)
00467       return (int)rowcount;
00468 
00469    return -1;
00470 }
00471 
00472 static int require_curl(const char *url, const char *unused, va_list ap)
00473 {
00474    struct ast_str *query;
00475    char *elm, field[256], buffer[128];
00476    int type, size;
00477    const int EncodeSpecialChars = 1;
00478 
00479    if (!ast_custom_function_find("CURL")) {
00480       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00481       return -1;
00482    }
00483 
00484    if (!(query = ast_str_create(100))) {
00485       return -1;
00486    }
00487 
00488    ast_str_set(&query, 0, "${CURL(%s/require,", url);
00489 
00490    while ((elm = va_arg(ap, char *))) {
00491       type = va_arg(ap, require_type);
00492       size = va_arg(ap, int);
00493       ast_uri_encode(elm, field, sizeof(field), EncodeSpecialChars);
00494       ast_str_append(&query, 0, "%s=%s%%3A%d", field,
00495          type == RQ_CHAR ? "char" :
00496          type == RQ_INTEGER1 ? "integer1" :
00497          type == RQ_UINTEGER1 ? "uinteger1" :
00498          type == RQ_INTEGER2 ? "integer2" :
00499          type == RQ_UINTEGER2 ? "uinteger2" :
00500          type == RQ_INTEGER3 ? "integer3" :
00501          type == RQ_UINTEGER3 ? "uinteger3" :
00502          type == RQ_INTEGER4 ? "integer4" :
00503          type == RQ_UINTEGER4 ? "uinteger4" :
00504          type == RQ_INTEGER8 ? "integer8" :
00505          type == RQ_UINTEGER8 ? "uinteger8" :
00506          type == RQ_DATE ? "date" :
00507          type == RQ_DATETIME ? "datetime" :
00508          type == RQ_FLOAT ? "float" :
00509          "unknown", size);
00510    }
00511    va_end(ap);
00512 
00513    ast_str_append(&query, 0, ")}");
00514    pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, sizeof(buffer) - 1);
00515    return atoi(buffer);
00516 }
00517 
00518 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, const char *who_asked)
00519 {
00520    struct ast_str *query;
00521    char buf1[200];
00522    char *stringp, *line, *pair, *key;
00523    const int EncodeSpecialChars = 1, bufsize = 256000;
00524    int last_cat_metric = -1, cat_metric = -1;
00525    struct ast_category *cat=NULL;
00526    char *buffer, *cur_cat = "";
00527    char *category = "", *var_name = "", *var_val = "";
00528    struct ast_flags loader_flags = { 0 };
00529 
00530    if (!ast_custom_function_find("CURL")) {
00531       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00532       return NULL;
00533    }
00534 
00535    if (!(query = ast_str_create(1000)))
00536       return NULL;
00537 
00538    if (!(buffer = ast_malloc(bufsize))) {
00539       ast_free(query);
00540       return NULL;
00541    }
00542 
00543    ast_uri_encode(file, buf1, sizeof(buf1), EncodeSpecialChars);
00544    ast_str_set(&query, 0, "${CURL(%s/static?file=%s)}", url, buf1);
00545 
00546    /* Do the CURL query */
00547    pbx_substitute_variables_helper(NULL, ast_str_buffer(query), buffer, bufsize - 1);
00548 
00549    /* Line oriented output */
00550    stringp = buffer;
00551    cat = ast_config_get_current_category(cfg);
00552 
00553    while ((line = strsep(&stringp, "\r\n"))) {
00554       if (ast_strlen_zero(line))
00555          continue;
00556 
00557       while ((pair = strsep(&line, "&"))) {
00558          key = strsep(&pair, "=");
00559          ast_uri_decode(key);
00560          if (pair)
00561             ast_uri_decode(pair);
00562 
00563          if (!strcasecmp(key, "category"))
00564             category = S_OR(pair, "");
00565          else if (!strcasecmp(key, "var_name"))
00566             var_name = S_OR(pair, "");
00567          else if (!strcasecmp(key, "var_val"))
00568             var_val = S_OR(pair, "");
00569          else if (!strcasecmp(key, "cat_metric"))
00570             cat_metric = pair ? atoi(pair) : 0;
00571       }
00572 
00573       if (!strcmp(var_name, "#include")) {
00574          if (!ast_config_internal_load(var_val, cfg, loader_flags, "", who_asked))
00575             return NULL;
00576       }
00577 
00578       if (strcmp(category, cur_cat) || last_cat_metric != cat_metric) {
00579          if (!(cat = ast_category_new(category, "", 99999)))
00580             break;
00581          cur_cat = category;
00582          last_cat_metric = cat_metric;
00583          ast_category_append(cfg, cat);
00584       }
00585       ast_variable_append(cat, ast_variable_new(var_name, var_val, ""));
00586    }
00587 
00588    ast_free(buffer);
00589    ast_free(query);
00590    return cfg;
00591 }
00592 
00593 static struct ast_config_engine curl_engine = {
00594    .name = "curl",
00595    .load_func = config_curl,
00596    .realtime_func = realtime_curl,
00597    .realtime_multi_func = realtime_multi_curl,
00598    .store_func = store_curl,
00599    .destroy_func = destroy_curl,
00600    .update_func = update_curl,
00601    .update2_func = update2_curl,
00602    .require_func = require_curl,
00603 };
00604 
00605 static int unload_module(void)
00606 {
00607    ast_config_engine_deregister(&curl_engine);
00608    ast_verb(1, "res_config_curl unloaded.\n");
00609    return 0;
00610 }
00611 
00612 static int load_module(void)
00613 {
00614    if (!ast_module_check("res_curl.so")) {
00615       if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00616          ast_log(LOG_ERROR, "Cannot load res_curl, so res_config_curl cannot be loaded\n");
00617          return AST_MODULE_LOAD_DECLINE;
00618       }
00619    }
00620 
00621    ast_config_engine_register(&curl_engine);
00622    ast_verb(1, "res_config_curl loaded.\n");
00623    return 0;
00624 }
00625 
00626 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Curl configuration");