Fri Jul 24 00:41:01 2009

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: 125055 $")
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, query->str, buffer, bufsize);
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, query->str, buffer, bufsize);
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, query->str, buffer, bufsize);
00262 
00263    /* Line oriented output */
00264    stringp = buffer;
00265    while (*stringp <= ' ')
00266       stringp++;
00267    sscanf(stringp, "%d", &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 /*!
00279  * \brief Execute an INSERT query
00280  * \param url
00281  * \param unused
00282  * \param ap list containing one or more field/value set(s)
00283  *
00284  * Insert a new record into database table, prepare the sql statement.
00285  * All values to be changed are stored in ap list.
00286  * Sub-in the values to the prepared statement and execute it.
00287  *
00288  * \retval number of rows affected
00289  * \retval -1 on failure
00290 */
00291 static int store_curl(const char *url, const char *unused, va_list ap)
00292 {
00293    struct ast_str *query;
00294    char buf1[200], buf2[200];
00295    const char *newparam, *newval;
00296    char *stringp;
00297    int i, rowcount = -1;
00298    const int EncodeSpecialChars = 1, bufsize = 100;
00299    char *buffer;
00300 
00301    if (!ast_custom_function_find("CURL")) {
00302       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00303       return -1;
00304    }
00305 
00306    if (!(query = ast_str_create(1000)))
00307       return -1;
00308 
00309    if (!(buffer = ast_malloc(bufsize))) {
00310       ast_free(query);
00311       return -1;
00312    }
00313 
00314    ast_str_set(&query, 0, "${CURL(%s/store,", url);
00315 
00316    for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00317       newval = va_arg(ap, const char *);
00318       ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
00319       ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
00320       ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00321    }
00322    va_end(ap);
00323 
00324    ast_str_append(&query, 0, ")}");
00325    pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize);
00326 
00327    stringp = buffer;
00328    while (*stringp <= ' ')
00329       stringp++;
00330    sscanf(stringp, "%d", &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 DELETE query
00343  * \param url
00344  * \param unused
00345  * \param keyfield where clause field
00346  * \param lookup value of field for where clause
00347  * \param ap list containing one or more field/value set(s)
00348  *
00349  * Delete a row from a database table, prepare the sql statement using keyfield and lookup
00350  * control the number of records to change. Additional params to match rows are stored in ap list.
00351  * Sub-in the values to the prepared statement and execute it.
00352  *
00353  * \retval number of rows affected
00354  * \retval -1 on failure
00355 */
00356 static int destroy_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
00357 {
00358    struct ast_str *query;
00359    char buf1[200], buf2[200];
00360    const char *newparam, *newval;
00361    char *stringp;
00362    int i, rowcount = -1;
00363    const int EncodeSpecialChars = 1, bufsize = 100;
00364    char *buffer;
00365 
00366    if (!ast_custom_function_find("CURL")) {
00367       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00368       return -1;
00369    }
00370 
00371    if (!(query = ast_str_create(1000)))
00372       return -1;
00373 
00374    if (!(buffer = ast_malloc(bufsize))) {
00375       ast_free(query);
00376       return -1;
00377    }
00378 
00379    ast_uri_encode(keyfield, buf1, sizeof(buf1), EncodeSpecialChars);
00380    ast_uri_encode(lookup, buf2, sizeof(buf2), EncodeSpecialChars);
00381    ast_str_set(&query, 0, "${CURL(%s/destroy,%s=%s&", url, buf1, buf2);
00382 
00383    for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00384       newval = va_arg(ap, const char *);
00385       ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars);
00386       ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars);
00387       ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00388    }
00389    va_end(ap);
00390 
00391    ast_str_append(&query, 0, ")}");
00392    pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize);
00393 
00394    /* Line oriented output */
00395    stringp = buffer;
00396    while (*stringp <= ' ')
00397       stringp++;
00398    sscanf(stringp, "%d", &rowcount);
00399 
00400    ast_free(buffer);
00401    ast_free(query);
00402 
00403    if (rowcount >= 0)
00404       return (int)rowcount;
00405 
00406    return -1;
00407 }
00408 
00409 static int require_curl(const char *url, const char *unused, va_list ap)
00410 {
00411    struct ast_str *query;
00412    char *elm, field[256], buffer[128];
00413    int type, size;
00414    const int EncodeSpecialChars = 1;
00415 
00416    if (!ast_custom_function_find("CURL")) {
00417       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00418       return -1;
00419    }
00420 
00421    if (!(query = ast_str_create(100))) {
00422       return -1;
00423    }
00424 
00425    ast_str_set(&query, 0, "${CURL(%s/require,", url);
00426 
00427    while ((elm = va_arg(ap, char *))) {
00428       type = va_arg(ap, require_type);
00429       size = va_arg(ap, int);
00430       ast_uri_encode(elm, field, sizeof(field), EncodeSpecialChars);
00431       ast_str_append(&query, 0, "%s=%s%%3A%d", field,
00432          type == RQ_CHAR ? "char" :
00433          type == RQ_INTEGER1 ? "integer1" :
00434          type == RQ_UINTEGER1 ? "uinteger1" :
00435          type == RQ_INTEGER2 ? "integer2" :
00436          type == RQ_UINTEGER2 ? "uinteger2" :
00437          type == RQ_INTEGER3 ? "integer3" :
00438          type == RQ_UINTEGER3 ? "uinteger3" :
00439          type == RQ_INTEGER4 ? "integer4" :
00440          type == RQ_UINTEGER4 ? "uinteger4" :
00441          type == RQ_INTEGER8 ? "integer8" :
00442          type == RQ_UINTEGER8 ? "uinteger8" :
00443          type == RQ_DATE ? "date" :
00444          type == RQ_DATETIME ? "datetime" :
00445          type == RQ_FLOAT ? "float" :
00446          "unknown", size);
00447    }
00448    va_end(ap);
00449 
00450    ast_str_append(&query, 0, ")}");
00451    pbx_substitute_variables_helper(NULL, query->str, buffer, sizeof(buffer));
00452    return atoi(buffer);
00453 }
00454 
00455 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)
00456 {
00457    struct ast_str *query;
00458    char buf1[200];
00459    char *stringp, *line, *pair, *key;
00460    const int EncodeSpecialChars = 1, bufsize = 256000;
00461    int last_cat_metric = -1, cat_metric = -1;
00462    struct ast_category *cat=NULL;
00463    char *buffer, *cur_cat = "";
00464    char *category = "", *var_name = "", *var_val = "";
00465    struct ast_flags loader_flags = { 0 };
00466 
00467    if (!ast_custom_function_find("CURL")) {
00468       ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00469       return NULL;
00470    }
00471 
00472    if (!(query = ast_str_create(1000)))
00473       return NULL;
00474 
00475    if (!(buffer = ast_malloc(bufsize))) {
00476       ast_free(query);
00477       return NULL;
00478    }
00479 
00480    ast_uri_encode(file, buf1, sizeof(buf1), EncodeSpecialChars);
00481    ast_str_set(&query, 0, "${CURL(%s/static?file=%s)}", url, buf1);
00482 
00483    /* Do the CURL query */
00484    pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize);
00485 
00486    /* Line oriented output */
00487    stringp = buffer;
00488    cat = ast_config_get_current_category(cfg);
00489 
00490    while ((line = strsep(&stringp, "\r\n"))) {
00491       if (ast_strlen_zero(line))
00492          continue;
00493 
00494       while ((pair = strsep(&line, "&"))) {
00495          key = strsep(&pair, "=");
00496          ast_uri_decode(key);
00497          if (pair)
00498             ast_uri_decode(pair);
00499 
00500          if (!strcasecmp(key, "category"))
00501             category = S_OR(pair, "");
00502          else if (!strcasecmp(key, "var_name"))
00503             var_name = S_OR(pair, "");
00504          else if (!strcasecmp(key, "var_val"))
00505             var_val = S_OR(pair, "");
00506          else if (!strcasecmp(key, "cat_metric"))
00507             cat_metric = pair ? atoi(pair) : 0;
00508       }
00509 
00510       if (!strcmp(var_name, "#include")) {
00511          if (!ast_config_internal_load(var_val, cfg, loader_flags, "", who_asked))
00512             return NULL;
00513       }
00514 
00515       if (strcmp(category, cur_cat) || last_cat_metric != cat_metric) {
00516          if (!(cat = ast_category_new(category, "", 99999)))
00517             break;
00518          cur_cat = category;
00519          last_cat_metric = cat_metric;
00520          ast_category_append(cfg, cat);
00521       }
00522       ast_variable_append(cat, ast_variable_new(var_name, var_val, ""));
00523    }
00524 
00525    ast_free(buffer);
00526    ast_free(query);
00527    return cfg;
00528 }
00529 
00530 static struct ast_config_engine curl_engine = {
00531    .name = "curl",
00532    .load_func = config_curl,
00533    .realtime_func = realtime_curl,
00534    .realtime_multi_func = realtime_multi_curl,
00535    .store_func = store_curl,
00536    .destroy_func = destroy_curl,
00537    .update_func = update_curl,
00538    .require_func = require_curl,
00539 };
00540 
00541 static int unload_module(void)
00542 {
00543    ast_config_engine_deregister(&curl_engine);
00544    ast_verb(1, "res_config_curl unloaded.\n");
00545    return 0;
00546 }
00547 
00548 static int load_module(void)
00549 {
00550    if (!ast_module_check("res_curl.so")) {
00551       if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00552          ast_log(LOG_ERROR, "Cannot load res_curl, so res_config_curl cannot be loaded\n");
00553          return AST_MODULE_LOAD_DECLINE;
00554       }
00555    }
00556 
00557    ast_config_engine_register(&curl_engine);
00558    ast_verb(1, "res_config_curl loaded.\n");
00559    return 0;
00560 }
00561 
00562 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Realtime Curl configuration");

Generated on Fri Jul 24 00:41:01 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7