static int update2_curl(const char *url, const char *unused, const struct ast_variable *lookup_fields, const struct ast_variable *update_fields) { struct ast_str *query, *buffer; char buf1[200], buf2[200]; const struct ast_variable *field; char *stringp; unsigned int start = 1; int rowcount = -1; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return -1; } if (!(query = ast_str_thread_get(&query_buf, 1000))) return -1; if (!(buffer = ast_str_thread_get(&result_buf, 16))) { return -1; } ast_str_set(&query, 0, "${CURL(%s/update?", url); for (field = lookup_fields; field; field = field->next) { ast_uri_encode(field->name, buf1, sizeof(buf1), ast_uri_http); ast_uri_encode(field->value, buf2, sizeof(buf2), ast_uri_http); ast_str_append(&query, 0, "%s%s=%s", !start ? "" : "&", buf1, buf2); start = 0; } ast_str_append(&query, 0, ","); start = 1; for (field = update_fields; field; field = field->next) { ast_uri_encode(field->name, buf1, sizeof(buf1), ast_uri_http); ast_uri_encode(field->value, buf2, sizeof(buf2), ast_uri_http); ast_str_append(&query, 0, "%s%s=%s", !start ? "" : "&", buf1, buf2); start = 0; } ast_str_append(&query, 0, ")}"); /* Proxies work, by setting CURLOPT options in the [globals] section of * extensions.conf. Unfortunately, this means preloading pbx_config.so * so that they have an opportunity to be set prior to startup realtime * queries. */ ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query)); /* Line oriented output */ stringp = ast_str_buffer(buffer); while (*stringp <= ' ') { stringp++; } sscanf(stringp, "%30d", &rowcount); if (rowcount >= 0) { return (int)rowcount; } return -1; }
/*! * \brief Execute an DELETE query * \param url * \param unused * \param keyfield where clause field * \param lookup value of field for where clause * \param ap list containing one or more field/value set(s) * * Delete a row from a database table, prepare the sql statement using keyfield and lookup * control the number of records to change. Additional params to match rows are stored in ap list. * Sub-in the values to the prepared statement and execute it. * * \retval number of rows affected * \retval -1 on failure */ static int destroy_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, va_list ap) { struct ast_str *query; char buf1[200], buf2[200]; const char *newparam, *newval; char *stringp; int i, rowcount = -1; const int EncodeSpecialChars = 1, bufsize = 100; char *buffer; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return -1; } if (!(query = ast_str_create(1000))) return -1; if (!(buffer = ast_malloc(bufsize))) { ast_free(query); return -1; } ast_uri_encode(keyfield, buf1, sizeof(buf1), EncodeSpecialChars); ast_uri_encode(lookup, buf2, sizeof(buf2), EncodeSpecialChars); ast_str_set(&query, 0, "${CURL(%s/destroy,%s=%s&", url, buf1, buf2); for (i = 0; (newparam = va_arg(ap, const char *)); i++) { newval = va_arg(ap, const char *); ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars); ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars); ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2); } va_end(ap); ast_str_append(&query, 0, ")}"); pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize); /* Line oriented output */ stringp = buffer; while (*stringp <= ' ') stringp++; sscanf(stringp, "%d", &rowcount); ast_free(buffer); ast_free(query); if (rowcount >= 0) return (int)rowcount; return -1; }
/*! * \brief Execute an DELETE query * \param url * \param unused * \param keyfield where clause field * \param lookup value of field for where clause * \param fields list containing one or more field/value set(s) * * Delete a row from a database table, prepare the sql statement using keyfield and lookup * control the number of records to change. Additional params to match rows are stored in ap list. * Sub-in the values to the prepared statement and execute it. * * \retval number of rows affected * \retval -1 on failure */ static int destroy_curl(const char *url, const char *unused, const char *keyfield, const char *lookup, const struct ast_variable *fields) { struct ast_str *query, *buffer; char buf1[200], buf2[200]; const struct ast_variable *field; char *stringp; int start = 1, rowcount = -1; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return -1; } if (!(query = ast_str_thread_get(&query_buf, 1000))) { return -1; } if (!(buffer = ast_str_thread_get(&result_buf, 16))) { return -1; } ast_uri_encode(keyfield, buf1, sizeof(buf1), ast_uri_http); ast_uri_encode(lookup, buf2, sizeof(buf2), ast_uri_http); ast_str_set(&query, 0, "${CURL(%s/destroy,%s=%s&", url, buf1, buf2); for (field = fields; field; field = field->next) { ast_uri_encode(field->name, buf1, sizeof(buf1), ast_uri_http); ast_uri_encode(field->value, buf2, sizeof(buf2), ast_uri_http); ast_str_append(&query, 0, "%s%s=%s", !start ? "&" : "", buf1, buf2); start = 0; } ast_str_append(&query, 0, ")}"); ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query)); /* Line oriented output */ stringp = ast_str_buffer(buffer); while (*stringp <= ' ') { stringp++; } sscanf(stringp, "%30d", &rowcount); if (rowcount >= 0) { return (int)rowcount; } return -1; }
/*! \brief uriencode: Encode URL according to RFC 2396 */ static int uriencode(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) { if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "Syntax: URIENCODE(<data>) - missing argument!\n"); return -1; } ast_uri_encode(data, buf, len, 1); return 0; }
/*! \brief uriencode: Encode URL according to RFC 2396 */ static int uriencode(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { if (ast_strlen_zero(data)) { buf[0] = '\0'; return 0; } ast_uri_encode(data, buf, len, ast_uri_http); return 0; }
static int require_curl(const char *url, const char *unused, va_list ap) { struct ast_str *query, *buffer; char *elm, field[256]; int type, size, i = 0; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return -1; } if (!(query = ast_str_thread_get(&query_buf, 100))) { return -1; } if (!(buffer = ast_str_thread_get(&result_buf, 16))) { return -1; } ast_str_set(&query, 0, "${CURL(%s/require,", url); while ((elm = va_arg(ap, char *))) { type = va_arg(ap, require_type); size = va_arg(ap, int); ast_uri_encode(elm, field, sizeof(field), ast_uri_http); ast_str_append(&query, 0, "%s%s=%s%%3A%d", i > 0 ? "&" : "", field, type == RQ_CHAR ? "char" : type == RQ_INTEGER1 ? "integer1" : type == RQ_UINTEGER1 ? "uinteger1" : type == RQ_INTEGER2 ? "integer2" : type == RQ_UINTEGER2 ? "uinteger2" : type == RQ_INTEGER3 ? "integer3" : type == RQ_UINTEGER3 ? "uinteger3" : type == RQ_INTEGER4 ? "integer4" : type == RQ_UINTEGER4 ? "uinteger4" : type == RQ_INTEGER8 ? "integer8" : type == RQ_UINTEGER8 ? "uinteger8" : type == RQ_DATE ? "date" : type == RQ_DATETIME ? "datetime" : type == RQ_FLOAT ? "float" : "unknown", size); i++; } ast_str_append(&query, 0, ")}"); ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query)); return atoi(ast_str_buffer(buffer)); }
void ast_ari_bridges_record(struct ast_variable *headers, struct ast_ari_bridges_record_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); RAII_VAR(struct ast_channel *, record_channel, NULL, ast_hangup); RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); RAII_VAR(char *, recording_url, NULL, ast_free); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct stasis_app_recording_options *, options, NULL, ao2_cleanup); RAII_VAR(char *, uri_encoded_name, NULL, ast_free); RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel); struct stasis_topic *channel_topic; struct stasis_topic *bridge_topic; size_t uri_name_maxlen; struct bridge_channel_control_thread_data *thread_data; pthread_t threadid; ast_assert(response != NULL); if (bridge == NULL) { return; } if (!(record_channel = prepare_bridge_media_channel("Recorder"))) { ast_ari_response_error( response, 500, "Internal Server Error", "Failed to create recording channel"); return; } bridge_topic = ast_bridge_topic(bridge); channel_topic = ast_channel_topic(record_channel); /* Forward messages from the recording channel topic to the bridge topic so that anything listening for * messages on the bridge topic will receive the recording start/stop messages. Other messages that would * go to this channel will be suppressed since the channel is marked as internal. */ if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) { ast_ari_response_error( response, 500, "Internal Error", "Could not forward record channel stasis messages to bridge topic"); return; } if (ast_unreal_channel_push_to_bridge(record_channel, bridge, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE | AST_BRIDGE_CHANNEL_FLAG_LONELY)) { ast_ari_response_error( response, 500, "Internal Error", "Failed to put recording channel into the bridge"); return; } control = stasis_app_control_create(record_channel); if (control == NULL) { ast_ari_response_alloc_failed(response); return; } options = stasis_app_recording_options_create(args->name, args->format); if (options == NULL) { ast_ari_response_alloc_failed(response); return; } ast_string_field_build(options, target, "bridge:%s", args->bridge_id); options->max_silence_seconds = args->max_silence_seconds; options->max_duration_seconds = args->max_duration_seconds; options->terminate_on = stasis_app_recording_termination_parse(args->terminate_on); options->if_exists = stasis_app_recording_if_exists_parse(args->if_exists); options->beep = args->beep; if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) { ast_ari_response_error( response, 400, "Bad Request", "terminateOn invalid"); return; } if (options->if_exists == AST_RECORD_IF_EXISTS_ERROR) { ast_ari_response_error( response, 400, "Bad Request", "ifExists invalid"); return; } if (!ast_get_format_for_file_ext(options->format)) { ast_ari_response_error( response, 422, "Unprocessable Entity", "specified format is unknown on this system"); return; } recording = stasis_app_control_record(control, options); if (recording == NULL) { switch(errno) { case EINVAL: /* While the arguments are invalid, we should have * caught them prior to calling record. */ ast_ari_response_error( response, 500, "Internal Server Error", "Error parsing request"); break; case EEXIST: ast_ari_response_error(response, 409, "Conflict", "Recording '%s' already exists and can not be overwritten", args->name); break; case ENOMEM: ast_ari_response_alloc_failed(response); break; case EPERM: ast_ari_response_error( response, 400, "Bad Request", "Recording name invalid"); break; default: ast_log(LOG_WARNING, "Unrecognized recording error: %s\n", strerror(errno)); ast_ari_response_error( response, 500, "Internal Server Error", "Internal Server Error"); break; } return; } uri_name_maxlen = strlen(args->name) * 3; uri_encoded_name = ast_malloc(uri_name_maxlen); if (!uri_encoded_name) { ast_ari_response_alloc_failed(response); return; } ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen, ast_uri_http); if (ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name) == -1) { recording_url = NULL; ast_ari_response_alloc_failed(response); return; } json = stasis_app_recording_to_json(recording); if (!json) { ast_ari_response_alloc_failed(response); return; } thread_data = ast_calloc(1, sizeof(*thread_data)); if (!thread_data) { ast_ari_response_alloc_failed(response); return; } thread_data->bridge_channel = record_channel; thread_data->control = control; thread_data->forward = channel_forward; if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) { ast_ari_response_alloc_failed(response); ast_free(thread_data); return; } /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */ record_channel = NULL; control = NULL; channel_forward = NULL; ast_ari_response_created(response, recording_url, ast_json_ref(json)); }
/*! * \brief Execute a curl query and return ast_variable list * \param url The base URL from which to retrieve data * \param unused Not currently used * \param fields list containing one or more field/operator/value set. * * \retval var on success * \retval NULL on failure */ static struct ast_variable *realtime_curl(const char *url, const char *unused, const struct ast_variable *fields) { struct ast_str *query, *buffer; char buf1[256], buf2[256]; const struct ast_variable *field; char *stringp, *pair, *key; unsigned int start = 1; struct ast_variable *var = NULL, *prev = NULL; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return NULL; } if (!(query = ast_str_thread_get(&query_buf, 16))) { return NULL; } if (!(buffer = ast_str_thread_get(&result_buf, 16))) { return NULL; } ast_str_set(&query, 0, "${CURL(%s/single,", url); for (field = fields; field; field = field->next) { ast_uri_encode(field->name, buf1, sizeof(buf1), ast_uri_http); ast_uri_encode(field->value, buf2, sizeof(buf2), ast_uri_http); ast_str_append(&query, 0, "%s%s=%s", !start ? "&" : "", buf1, buf2); start = 0; } ast_str_append(&query, 0, ")}"); ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query)); /* Remove any trailing newline characters */ if ((stringp = strchr(ast_str_buffer(buffer), '\r')) || (stringp = strchr(ast_str_buffer(buffer), '\n'))) { *stringp = '\0'; } stringp = ast_str_buffer(buffer); while ((pair = strsep(&stringp, "&"))) { key = strsep(&pair, "="); ast_uri_decode(key, ast_uri_http); if (pair) { ast_uri_decode(pair, ast_uri_http); } if (!ast_strlen_zero(key)) { if (prev) { prev->next = ast_variable_new(key, S_OR(pair, ""), ""); if (prev->next) { prev = prev->next; } } else { prev = var = ast_variable_new(key, S_OR(pair, ""), ""); } } } return var; }
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) { struct ast_str *query, *buffer; char buf1[200]; char *stringp, *line, *pair, *key; int last_cat_metric = -1, cat_metric = -1; struct ast_category *cat = NULL; char *cur_cat = ""; char *category = "", *var_name = "", *var_val = ""; struct ast_flags loader_flags = { 0 }; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return NULL; } if (!(query = ast_str_thread_get(&query_buf, 100))) { return NULL; } if (!(buffer = ast_str_thread_get(&result_buf, 16))) { return NULL; } ast_uri_encode(file, buf1, sizeof(buf1), ast_uri_http); ast_str_set(&query, 0, "${CURL(%s/static?file=%s)}", url, buf1); /* Do the CURL query */ ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query)); /* Line oriented output */ stringp = ast_str_buffer(buffer); cat = ast_config_get_current_category(cfg); while ((line = strsep(&stringp, "\r\n"))) { if (ast_strlen_zero(line)) { continue; } while ((pair = strsep(&line, "&"))) { key = strsep(&pair, "="); ast_uri_decode(key, ast_uri_http); if (pair) { ast_uri_decode(pair, ast_uri_http); } if (!strcasecmp(key, "category")) { category = S_OR(pair, ""); } else if (!strcasecmp(key, "var_name")) { var_name = S_OR(pair, ""); } else if (!strcasecmp(key, "var_val")) { var_val = S_OR(pair, ""); } else if (!strcasecmp(key, "cat_metric")) { cat_metric = pair ? atoi(pair) : 0; } } if (!strcmp(var_name, "#include")) { if (!ast_config_internal_load(var_val, cfg, loader_flags, "", who_asked)) return NULL; } if (!cat || strcmp(category, cur_cat) || last_cat_metric != cat_metric) { if (!(cat = ast_category_new(category, "", 99999))) break; cur_cat = category; last_cat_metric = cat_metric; ast_category_append(cfg, cat); } ast_variable_append(cat, ast_variable_new(var_name, var_val, "")); } return cfg; }
/*! * \brief Excute an Select query and return ast_config list * \param url * \param unused * \param fields list containing one or more field/operator/value set. * * \retval struct ast_config pointer on success * \retval NULL on failure */ static struct ast_config *realtime_multi_curl(const char *url, const char *unused, const struct ast_variable *fields) { struct ast_str *query, *buffer; char buf1[256], buf2[256]; const struct ast_variable *field; char *stringp, *line, *pair, *key, *initfield = NULL; int start = 1; struct ast_variable *var = NULL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return NULL; } if (!(query = ast_str_thread_get(&query_buf, 16))) { return NULL; } if (!(buffer = ast_str_thread_get(&result_buf, 16))) { return NULL; } ast_str_set(&query, 0, "${CURL(%s/multi,", url); for (field = fields; field; field = field->next) { if (start) { char *op; initfield = ast_strdupa(field->name); if ((op = strchr(initfield, ' '))) *op = '\0'; } ast_uri_encode(field->name, buf1, sizeof(buf1), ast_uri_http); ast_uri_encode(field->value, buf2, sizeof(buf2), ast_uri_http); ast_str_append(&query, 0, "%s%s=%s", !start ? "&" : "", buf1, buf2); start = 0; } ast_str_append(&query, 0, ")}"); /* Do the CURL query */ ast_str_substitute_variables(&buffer, 0, NULL, ast_str_buffer(query)); if (!(cfg = ast_config_new())) { return NULL; } /* Line oriented output */ stringp = ast_str_buffer(buffer); while ((line = strsep(&stringp, "\r\n"))) { if (ast_strlen_zero(line)) { continue; } if (!(cat = ast_category_new("", "", 99999))) { continue; } while ((pair = strsep(&line, "&"))) { key = strsep(&pair, "="); ast_uri_decode(key, ast_uri_http); if (pair) { ast_uri_decode(pair, ast_uri_http); } if (!strcasecmp(key, initfield) && pair) { ast_category_rename(cat, pair); } if (!ast_strlen_zero(key)) { var = ast_variable_new(key, S_OR(pair, ""), ""); ast_variable_append(cat, var); } } ast_category_append(cfg, cat); } return cfg; }
void ast_ari_channels_record(struct ast_variable *headers, struct ast_ari_channels_record_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); RAII_VAR(char *, recording_url, NULL, ast_free); struct ast_json *json; RAII_VAR(struct stasis_app_recording_options *, options, NULL, ao2_cleanup); RAII_VAR(char *, uri_encoded_name, NULL, ast_free); size_t uri_name_maxlen; ast_assert(response != NULL); if (args->max_duration_seconds < 0) { ast_ari_response_error( response, 400, "Bad Request", "max_duration_seconds cannot be negative"); return; } if (args->max_silence_seconds < 0) { ast_ari_response_error( response, 400, "Bad Request", "max_silence_seconds cannot be negative"); return; } control = find_control(response, args->channel_id); if (control == NULL) { /* Response filled in by find_control */ return; } options = stasis_app_recording_options_create(args->name, args->format); if (options == NULL) { ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); } ast_string_field_build(options, target, "channel:%s", args->channel_id); options->max_silence_seconds = args->max_silence_seconds; options->max_duration_seconds = args->max_duration_seconds; options->terminate_on = stasis_app_recording_termination_parse(args->terminate_on); options->if_exists = stasis_app_recording_if_exists_parse(args->if_exists); options->beep = args->beep; if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) { ast_ari_response_error( response, 400, "Bad Request", "terminateOn invalid"); return; } if (options->if_exists == -1) { ast_ari_response_error( response, 400, "Bad Request", "ifExists invalid"); return; } if (!ast_get_format_for_file_ext(options->format)) { ast_ari_response_error( response, 422, "Unprocessable Entity", "specified format is unknown on this system"); return; } recording = stasis_app_control_record(control, options); if (recording == NULL) { switch(errno) { case EINVAL: /* While the arguments are invalid, we should have * caught them prior to calling record. */ ast_ari_response_error( response, 500, "Internal Server Error", "Error parsing request"); break; case EEXIST: ast_ari_response_error(response, 409, "Conflict", "Recording '%s' already exists and can not be overwritten", args->name); break; case ENOMEM: ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); break; case EPERM: ast_ari_response_error( response, 400, "Bad Request", "Recording name invalid"); break; default: ast_log(LOG_WARNING, "Unrecognized recording error: %s\n", strerror(errno)); ast_ari_response_error( response, 500, "Internal Server Error", "Internal Server Error"); break; } return; } uri_name_maxlen = strlen(args->name) * 3; uri_encoded_name = ast_malloc(uri_name_maxlen); if (!uri_encoded_name) { ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); return; } ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen, ast_uri_http); if (ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name) == -1) { recording_url = NULL; ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); return; } json = stasis_app_recording_to_json(recording); if (!json) { ast_ari_response_error( response, 500, "Internal Server Error", "Out of memory"); return; } ast_ari_response_created(response, recording_url, json); }
/*! * \brief Execute a curl query and return ast_variable list * \param url The base URL from which to retrieve data * \param unused Not currently used * \param ap list containing one or more field/operator/value set. * * \retval var on success * \retval NULL on failure */ static struct ast_variable *realtime_curl(const char *url, const char *unused, va_list ap) { struct ast_str *query; char buf1[200], buf2[200]; const char *newparam, *newval; char *stringp, *pair, *key; int i; struct ast_variable *var=NULL, *prev=NULL; const int EncodeSpecialChars = 1, bufsize = 64000; char *buffer; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return NULL; } if (!(query = ast_str_create(1000))) return NULL; if (!(buffer = ast_malloc(bufsize))) { ast_free(query); return NULL; } ast_str_set(&query, 0, "${CURL(%s/single,", url); for (i = 0; (newparam = va_arg(ap, const char *)); i++) { newval = va_arg(ap, const char *); ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars); ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars); ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2); } va_end(ap); ast_str_append(&query, 0, ")}"); pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize); /* Remove any trailing newline characters */ if ((stringp = strchr(buffer, '\r')) || (stringp = strchr(buffer, '\n'))) *stringp = '\0'; stringp = buffer; while ((pair = strsep(&stringp, "&"))) { key = strsep(&pair, "="); ast_uri_decode(key); if (pair) ast_uri_decode(pair); if (!ast_strlen_zero(key)) { if (prev) { prev->next = ast_variable_new(key, S_OR(pair, ""), ""); if (prev->next) prev = prev->next; } else prev = var = ast_variable_new(key, S_OR(pair, ""), ""); } } ast_free(buffer); ast_free(query); return var; }
/*! * \brief Excute an Select query and return ast_config list * \param url * \param unused * \param ap list containing one or more field/operator/value set. * * \retval struct ast_config pointer on success * \retval NULL on failure */ static struct ast_config *realtime_multi_curl(const char *url, const char *unused, va_list ap) { struct ast_str *query; char buf1[200], buf2[200]; const char *newparam, *newval; char *stringp, *line, *pair, *key, *initfield = NULL; int i; const int EncodeSpecialChars = 1, bufsize = 256000; struct ast_variable *var=NULL; struct ast_config *cfg=NULL; struct ast_category *cat=NULL; char *buffer; if (!ast_custom_function_find("CURL")) { ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); return NULL; } if (!(query = ast_str_create(1000))) return NULL; if (!(buffer = ast_malloc(bufsize))) { ast_free(query); return NULL; } ast_str_set(&query, 0, "${CURL(%s/multi,", url); for (i = 0; (newparam = va_arg(ap, const char *)); i++) { newval = va_arg(ap, const char *); if (i == 0) { char *op; initfield = ast_strdupa(newparam); if ((op = strchr(initfield, ' '))) *op = '\0'; } ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars); ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars); ast_str_append(&query, 0, "%s%s=%s", i > 0 ? "&" : "", buf1, buf2); } va_end(ap); ast_str_append(&query, 0, ")}"); /* Do the CURL query */ pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize); if (!(cfg = ast_config_new())) goto exit_multi; /* Line oriented output */ stringp = buffer; while ((line = strsep(&stringp, "\r\n"))) { if (ast_strlen_zero(line)) continue; if (!(cat = ast_category_new("", "", 99999))) continue; while ((pair = strsep(&line, "&"))) { key = strsep(&pair, "="); ast_uri_decode(key); if (pair) ast_uri_decode(pair); if (!strcasecmp(key, initfield) && pair) ast_category_rename(cat, pair); if (!ast_strlen_zero(key)) { var = ast_variable_new(key, S_OR(pair, ""), ""); ast_variable_append(cat, var); } } ast_category_append(cfg, cat); } exit_multi: ast_free(buffer); ast_free(query); return cfg; }