00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include "asterisk.h"
00035
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 328259 $")
00037
00038 #include <curl/curl.h>
00039
00040 #include "asterisk/file.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/pbx.h"
00043 #include "asterisk/config.h"
00044 #include "asterisk/module.h"
00045 #include "asterisk/lock.h"
00046 #include "asterisk/utils.h"
00047 #include "asterisk/threadstorage.h"
00048
00049 AST_THREADSTORAGE(query_buf);
00050 AST_THREADSTORAGE(result_buf);
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061 static struct ast_variable *realtime_curl(const char *url, const char *unused, va_list ap)
00062 {
00063 struct ast_str *query, *buffer;
00064 char buf1[256], buf2[256];
00065 const char *newparam, *newval;
00066 char *stringp, *pair, *key;
00067 int i;
00068 struct ast_variable *var = NULL, *prev = NULL;
00069
00070 if (!ast_custom_function_find("CURL")) {
00071 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00072 return NULL;
00073 }
00074
00075 if (!(query = ast_str_thread_get(&query_buf, 16))) {
00076 return NULL;
00077 }
00078
00079 if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
00080 return NULL;
00081 }
00082
00083 ast_str_set(&query, 0, "${CURL(%s/single,", url);
00084
00085 for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00086 newval = va_arg(ap, const char *);
00087 ast_uri_encode(newparam, buf1, sizeof(buf1), ast_uri_http);
00088 ast_uri_encode(newval, buf2, sizeof(buf2), ast_uri_http);
00089 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00090 }
00091 va_end(ap);
00092
00093 ast_str_append(&query, 0, ")}");
00094 ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
00095
00096
00097 if ((stringp = strchr(ast_str_buffer(buffer), '\r')) || (stringp = strchr(ast_str_buffer(buffer), '\n'))) {
00098 *stringp = '\0';
00099 }
00100
00101 stringp = ast_str_buffer(buffer);
00102 while ((pair = strsep(&stringp, "&"))) {
00103 key = strsep(&pair, "=");
00104 ast_uri_decode(key, ast_uri_http);
00105 if (pair) {
00106 ast_uri_decode(pair, ast_uri_http);
00107 }
00108
00109 if (!ast_strlen_zero(key)) {
00110 if (prev) {
00111 prev->next = ast_variable_new(key, S_OR(pair, ""), "");
00112 if (prev->next) {
00113 prev = prev->next;
00114 }
00115 } else {
00116 prev = var = ast_variable_new(key, S_OR(pair, ""), "");
00117 }
00118 }
00119 }
00120
00121 return var;
00122 }
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 static struct ast_config *realtime_multi_curl(const char *url, const char *unused, va_list ap)
00134 {
00135 struct ast_str *query, *buffer;
00136 char buf1[256], buf2[256];
00137 const char *newparam, *newval;
00138 char *stringp, *line, *pair, *key, *initfield = NULL;
00139 int i;
00140 struct ast_variable *var = NULL;
00141 struct ast_config *cfg = NULL;
00142 struct ast_category *cat = NULL;
00143
00144 if (!ast_custom_function_find("CURL")) {
00145 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00146 return NULL;
00147 }
00148
00149 if (!(query = ast_str_thread_get(&query_buf, 16))) {
00150 return NULL;
00151 }
00152
00153 if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
00154 return NULL;
00155 }
00156
00157 ast_str_set(&query, 0, "${CURL(%s/multi,", url);
00158
00159 for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00160 newval = va_arg(ap, const char *);
00161 if (i == 0) {
00162 char *op;
00163 initfield = ast_strdupa(newparam);
00164 if ((op = strchr(initfield, ' ')))
00165 *op = '\0';
00166 }
00167 ast_uri_encode(newparam, buf1, sizeof(buf1), ast_uri_http);
00168 ast_uri_encode(newval, buf2, sizeof(buf2), ast_uri_http);
00169 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00170 }
00171 va_end(ap);
00172
00173 ast_str_append(&query, 0, ")}");
00174
00175
00176 ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
00177
00178 if (!(cfg = ast_config_new())) {
00179 return NULL;
00180 }
00181
00182
00183 stringp = ast_str_buffer(buffer);
00184 while ((line = strsep(&stringp, "\r\n"))) {
00185 if (ast_strlen_zero(line)) {
00186 continue;
00187 }
00188
00189 if (!(cat = ast_category_new("", "", 99999))) {
00190 continue;
00191 }
00192
00193 while ((pair = strsep(&line, "&"))) {
00194 key = strsep(&pair, "=");
00195 ast_uri_decode(key, ast_uri_http);
00196 if (pair) {
00197 ast_uri_decode(pair, ast_uri_http);
00198 }
00199
00200 if (!strcasecmp(key, initfield) && pair) {
00201 ast_category_rename(cat, pair);
00202 }
00203
00204 if (!ast_strlen_zero(key)) {
00205 var = ast_variable_new(key, S_OR(pair, ""), "");
00206 ast_variable_append(cat, var);
00207 }
00208 }
00209 ast_category_append(cfg, cat);
00210 }
00211
00212 return cfg;
00213 }
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230 static int update_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
00231 {
00232 struct ast_str *query, *buffer;
00233 char buf1[256], buf2[256];
00234 const char *newparam, *newval;
00235 char *stringp;
00236 int i, rowcount = -1;
00237
00238 if (!ast_custom_function_find("CURL")) {
00239 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00240 return -1;
00241 }
00242
00243 if (!(query = ast_str_thread_get(&query_buf, 16))) {
00244 return -1;
00245 }
00246
00247 if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
00248 return -1;
00249 }
00250
00251 ast_uri_encode(keyfield, buf1, sizeof(buf1), ast_uri_http);
00252 ast_uri_encode(lookup, buf2, sizeof(buf2), ast_uri_http);
00253 ast_str_set(&query, 0, "${CURL(%s/update?%s=%s,", url, buf1, buf2);
00254
00255 for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00256 newval = va_arg(ap, const char *);
00257 ast_uri_encode(newparam, buf1, sizeof(buf1), ast_uri_http);
00258 ast_uri_encode(newval, buf2, sizeof(buf2), ast_uri_http);
00259 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00260 }
00261 va_end(ap);
00262
00263 ast_str_append(&query, 0, ")}");
00264 ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
00265
00266
00267 stringp = ast_str_buffer(buffer);
00268 while (*stringp <= ' ') {
00269 stringp++;
00270 }
00271 sscanf(stringp, "%30d", &rowcount);
00272
00273 if (rowcount >= 0) {
00274 return (int)rowcount;
00275 }
00276
00277 return -1;
00278 }
00279
00280 static int update2_curl(const char *url, const char *unused, va_list ap)
00281 {
00282 struct ast_str *query, *buffer;
00283 char buf1[200], buf2[200];
00284 const char *newparam, *newval;
00285 char *stringp;
00286 int rowcount = -1, lookup = 1, first = 1;
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_thread_get(&query_buf, 1000)))
00294 return -1;
00295
00296 if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
00297 return -1;
00298 }
00299
00300 ast_str_set(&query, 0, "${CURL(%s/update?", url);
00301
00302 for (;;) {
00303 if ((newparam = va_arg(ap, const char *)) == SENTINEL) {
00304 if (lookup) {
00305 lookup = 0;
00306 ast_str_append(&query, 0, ",");
00307
00308 first = 1;
00309 continue;
00310 } else {
00311 break;
00312 }
00313 }
00314 newval = va_arg(ap, const char *);
00315 ast_uri_encode(newparam, buf1, sizeof(buf1), ast_uri_http);
00316 ast_uri_encode(newval, buf2, sizeof(buf2), ast_uri_http);
00317 ast_str_append(&query, 0, "%s%s=%s", first ? "" : "&", buf1, buf2);
00318 first = 0;
00319 }
00320 va_end(ap);
00321
00322 ast_str_append(&query, 0, ")}");
00323
00324
00325
00326
00327 ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
00328
00329
00330 stringp = ast_str_buffer(buffer);
00331 while (*stringp <= ' ') {
00332 stringp++;
00333 }
00334 sscanf(stringp, "%30d", &rowcount);
00335
00336 if (rowcount >= 0) {
00337 return (int)rowcount;
00338 }
00339
00340 return -1;
00341 }
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356 static int store_curl(const char *url, const char *unused, va_list ap)
00357 {
00358 struct ast_str *query, *buffer;
00359 char buf1[256], buf2[256];
00360 const char *newparam, *newval;
00361 char *stringp;
00362 int i, rowcount = -1;
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_thread_get(&query_buf, 1000))) {
00370 return -1;
00371 }
00372
00373 if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
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), ast_uri_http);
00382 ast_uri_encode(newval, buf2, sizeof(buf2), ast_uri_http);
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 ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
00389
00390 stringp = ast_str_buffer(buffer);
00391 while (*stringp <= ' ') {
00392 stringp++;
00393 }
00394 sscanf(stringp, "%30d", &rowcount);
00395
00396 if (rowcount >= 0) {
00397 return rowcount;
00398 }
00399
00400 return -1;
00401 }
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418 static int destroy_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap)
00419 {
00420 struct ast_str *query, *buffer;
00421 char buf1[200], buf2[200];
00422 const char *newparam, *newval;
00423 char *stringp;
00424 int i, rowcount = -1;
00425
00426 if (!ast_custom_function_find("CURL")) {
00427 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00428 return -1;
00429 }
00430
00431 if (!(query = ast_str_thread_get(&query_buf, 1000))) {
00432 return -1;
00433 }
00434
00435 if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
00436 return -1;
00437 }
00438
00439 ast_uri_encode(keyfield, buf1, sizeof(buf1), ast_uri_http);
00440 ast_uri_encode(lookup, buf2, sizeof(buf2), ast_uri_http);
00441 ast_str_set(&query, 0, "${CURL(%s/destroy,%s=%s&", url, buf1, buf2);
00442
00443 for (i = 0; (newparam = va_arg(ap, const char *)); i++) {
00444 newval = va_arg(ap, const char *);
00445 ast_uri_encode(newparam, buf1, sizeof(buf1), ast_uri_http);
00446 ast_uri_encode(newval, buf2, sizeof(buf2), ast_uri_http);
00447 ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2);
00448 }
00449 va_end(ap);
00450
00451 ast_str_append(&query, 0, ")}");
00452 ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
00453
00454
00455 stringp = ast_str_buffer(buffer);
00456 while (*stringp <= ' ') {
00457 stringp++;
00458 }
00459 sscanf(stringp, "%30d", &rowcount);
00460
00461 if (rowcount >= 0) {
00462 return (int)rowcount;
00463 }
00464
00465 return -1;
00466 }
00467
00468 static int require_curl(const char *url, const char *unused, va_list ap)
00469 {
00470 struct ast_str *query, *buffer;
00471 char *elm, field[256];
00472 int type, size;
00473
00474 if (!ast_custom_function_find("CURL")) {
00475 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00476 return -1;
00477 }
00478
00479 if (!(query = ast_str_thread_get(&query_buf, 100))) {
00480 return -1;
00481 }
00482
00483 if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
00484 return -1;
00485 }
00486
00487 ast_str_set(&query, 0, "${CURL(%s/require,", url);
00488
00489 while ((elm = va_arg(ap, char *))) {
00490 type = va_arg(ap, require_type);
00491 size = va_arg(ap, int);
00492 ast_uri_encode(elm, field, sizeof(field), ast_uri_http);
00493 ast_str_append(&query, 0, "%s=%s%%3A%d", field,
00494 type == RQ_CHAR ? "char" :
00495 type == RQ_INTEGER1 ? "integer1" :
00496 type == RQ_UINTEGER1 ? "uinteger1" :
00497 type == RQ_INTEGER2 ? "integer2" :
00498 type == RQ_UINTEGER2 ? "uinteger2" :
00499 type == RQ_INTEGER3 ? "integer3" :
00500 type == RQ_UINTEGER3 ? "uinteger3" :
00501 type == RQ_INTEGER4 ? "integer4" :
00502 type == RQ_UINTEGER4 ? "uinteger4" :
00503 type == RQ_INTEGER8 ? "integer8" :
00504 type == RQ_UINTEGER8 ? "uinteger8" :
00505 type == RQ_DATE ? "date" :
00506 type == RQ_DATETIME ? "datetime" :
00507 type == RQ_FLOAT ? "float" :
00508 "unknown", size);
00509 }
00510 va_end(ap);
00511
00512 ast_str_append(&query, 0, ")}");
00513 ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
00514 return atoi(ast_str_buffer(buffer));
00515 }
00516
00517 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)
00518 {
00519 struct ast_str *query, *buffer;
00520 char buf1[200];
00521 char *stringp, *line, *pair, *key;
00522 int last_cat_metric = -1, cat_metric = -1;
00523 struct ast_category *cat = NULL;
00524 char *cur_cat = "";
00525 char *category = "", *var_name = "", *var_val = "";
00526 struct ast_flags loader_flags = { 0 };
00527
00528 if (!ast_custom_function_find("CURL")) {
00529 ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n");
00530 return NULL;
00531 }
00532
00533 if (!(query = ast_str_thread_get(&query_buf, 100))) {
00534 return NULL;
00535 }
00536
00537 if (!(buffer = ast_str_thread_get(&result_buf, 16))) {
00538 return NULL;
00539 }
00540
00541 ast_uri_encode(file, buf1, sizeof(buf1), ast_uri_http);
00542 ast_str_set(&query, 0, "${CURL(%s/static?file=%s)}", url, buf1);
00543
00544
00545 ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query));
00546
00547
00548 stringp = ast_str_buffer(buffer);
00549 cat = ast_config_get_current_category(cfg);
00550
00551 while ((line = strsep(&stringp, "\r\n"))) {
00552 if (ast_strlen_zero(line)) {
00553 continue;
00554 }
00555
00556 while ((pair = strsep(&line, "&"))) {
00557 key = strsep(&pair, "=");
00558 ast_uri_decode(key, ast_uri_http);
00559 if (pair) {
00560 ast_uri_decode(pair, ast_uri_http);
00561 }
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
00574 if (!strcmp(var_name, "#include")) {
00575 if (!ast_config_internal_load(var_val, cfg, loader_flags, "", who_asked))
00576 return NULL;
00577 }
00578
00579 if (!cat || strcmp(category, cur_cat) || last_cat_metric != cat_metric) {
00580 if (!(cat = ast_category_new(category, "", 99999)))
00581 break;
00582 cur_cat = category;
00583 last_cat_metric = cat_metric;
00584 ast_category_append(cfg, cat);
00585 }
00586 ast_variable_append(cat, ast_variable_new(var_name, var_val, ""));
00587 }
00588
00589 return cfg;
00590 }
00591
00592 static struct ast_config_engine curl_engine = {
00593 .name = "curl",
00594 .load_func = config_curl,
00595 .realtime_func = realtime_curl,
00596 .realtime_multi_func = realtime_multi_curl,
00597 .store_func = store_curl,
00598 .destroy_func = destroy_curl,
00599 .update_func = update_curl,
00600 .update2_func = update2_curl,
00601 .require_func = require_curl,
00602 };
00603
00604 static int reload_module(void)
00605 {
00606 struct ast_flags flags = { CONFIG_FLAG_NOREALTIME };
00607 struct ast_config *cfg;
00608 struct ast_variable *var;
00609
00610 if (!(cfg = ast_config_load("res_curl.conf", flags))) {
00611 return 0;
00612 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
00613 ast_log(LOG_WARNING, "res_curl.conf could not be parsed!\n");
00614 return 0;
00615 }
00616
00617 if (!(var = ast_variable_browse(cfg, "globals")) && !(var = ast_variable_browse(cfg, "global")) && !(var = ast_variable_browse(cfg, "general"))) {
00618 ast_log(LOG_WARNING, "[globals] not found in res_curl.conf\n");
00619 ast_config_destroy(cfg);
00620 return 0;
00621 }
00622
00623 for (; var; var = var->next) {
00624 if (strncmp(var->name, "CURLOPT(", 8)) {
00625 char name[256];
00626 snprintf(name, sizeof(name), "CURLOPT(%s)", var->name);
00627 pbx_builtin_setvar_helper(NULL, name, var->value);
00628 } else {
00629 pbx_builtin_setvar_helper(NULL, var->name, var->value);
00630 }
00631 }
00632 ast_config_destroy(cfg);
00633 return 0;
00634 }
00635
00636 static int unload_module(void)
00637 {
00638 ast_config_engine_deregister(&curl_engine);
00639 ast_verb(1, "res_config_curl unloaded.\n");
00640 return 0;
00641 }
00642
00643 static int load_module(void)
00644 {
00645 if (!ast_module_check("res_curl.so")) {
00646 if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00647 ast_log(LOG_ERROR, "Cannot load res_curl, so res_config_curl cannot be loaded\n");
00648 return AST_MODULE_LOAD_DECLINE;
00649 }
00650 }
00651
00652 if (!ast_module_check("func_curl.so")) {
00653 if (ast_load_resource("func_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00654 ast_log(LOG_ERROR, "Cannot load func_curl, so res_config_curl cannot be loaded\n");
00655 return AST_MODULE_LOAD_DECLINE;
00656 }
00657 }
00658
00659 reload_module();
00660
00661 ast_config_engine_register(&curl_engine);
00662 ast_verb(1, "res_config_curl loaded.\n");
00663 return 0;
00664 }
00665
00666 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Realtime Curl configuration",
00667 .load = load_module,
00668 .unload = unload_module,
00669 .reload = reload_module,
00670 .load_pri = AST_MODPRI_REALTIME_DRIVER,
00671 );