コード例 #1
0
static enum ast_test_result_state test_2way_function(struct ast_test *test,
		struct ast_channel *c, const char *encode1, const char *encode2,
		const char *decode1, const char *decode2)
{
	struct ast_str *str = ast_str_create(16), *expression = ast_str_alloca(120);
	int okay;

	ast_str_set(&expression, 0, "%s%s%s", encode1, "foobarbaz", encode2);
	ast_str_substitute_variables(&str, 0, c, ast_str_buffer(expression));
	ast_str_set(&expression, 0, "%s%s%s", decode1, ast_str_buffer(str), decode2);
	ast_str_substitute_variables(&str, 0, c, ast_str_buffer(expression));

	okay = !strcmp(ast_str_buffer(str), "foobarbaz");

	ast_test_status_update(test, "Testing '%s%s' and '%s%s' . . . . . %s\n",
			encode1, encode2, decode1, decode2,
			okay ? "passed" : "FAILED");

	if (!okay) {
		ast_test_status_update(test, "  '%s' != 'foobarbaz'\n",
				ast_str_buffer(str));
	}

	ast_free(str);

	return okay ? AST_TEST_PASS : AST_TEST_FAIL;
}
コード例 #2
0
ファイル: sccp_netsock.c プロジェクト: macntouch/chan-sccp-b
char *__netsock_stringify_fmt(const struct sockaddr_storage *sockAddrStorage, int format)
{
	const struct sockaddr_storage *sockAddrStorage_tmp;
	char host[NI_MAXHOST] = "";
	char port[NI_MAXSERV] = "";
	struct ast_str *str;
	int e;
	static const size_t size = sizeof(host) - 1 + sizeof(port) - 1 + 4;

	if (!sockAddrStorage) {
		return "(null)";
	}

	if (!(str = ast_str_thread_get(&sccp_netsock_stringify_buf, size))) {
		return "";
	}
	//if (sccp_netsock_ipv4_mapped(sockAddrStorage, &sockAddrStorage_tmp_ipv4)) {
	//	struct sockaddr_storage sockAddrStorage_tmp_ipv4;
	//	sockAddrStorage_tmp = &sockAddrStorage_tmp_ipv4;
	//#if DEBUG
	//	sccp_log(0)("SCCP: ipv4 mapped in ipv6 address\n");
	//#endif
	//} else {
	sockAddrStorage_tmp = sockAddrStorage;
	//}

	if ((e = getnameinfo((struct sockaddr *) sockAddrStorage_tmp, sccp_netsock_sizeof(sockAddrStorage_tmp), format & SCCP_SOCKADDR_STR_ADDR ? host : NULL, format & SCCP_SOCKADDR_STR_ADDR ? sizeof(host) : 0, format & SCCP_SOCKADDR_STR_PORT ? port : 0, format & SCCP_SOCKADDR_STR_PORT ? sizeof(port) : 0, NI_NUMERICHOST | NI_NUMERICSERV))) {
		sccp_log(DEBUGCAT_SOCKET) (VERBOSE_PREFIX_3 "SCCP: getnameinfo(): %s \n", gai_strerror(e));
		return "";
	}

	if ((format & SCCP_SOCKADDR_STR_REMOTE) == SCCP_SOCKADDR_STR_REMOTE) {
		char *p;

		if (sccp_netsock_is_ipv6_link_local(sockAddrStorage_tmp) && (p = strchr(host, '%'))) {
			*p = '\0';
		}
	}
	switch ((format & SCCP_SOCKADDR_STR_FORMAT_MASK)) {
		case SCCP_SOCKADDR_STR_DEFAULT:
			ast_str_set(&str, 0, sockAddrStorage_tmp->ss_family == AF_INET6 ? "[%s]:%s" : "%s:%s", host, port);
			break;
		case SCCP_SOCKADDR_STR_ADDR:
			ast_str_set(&str, 0, "%s", host);
			break;
		case SCCP_SOCKADDR_STR_HOST:
			ast_str_set(&str, 0, sockAddrStorage_tmp->ss_family == AF_INET6 ? "[%s]" : "%s", host);
			break;
		case SCCP_SOCKADDR_STR_PORT:
			ast_str_set(&str, 0, "%s", port);
			break;
		default:
			pbx_log(LOG_ERROR, "Invalid format\n");
			return "";
	}

	return ast_str_buffer(str);
}
コード例 #3
0
static void security_event_cb(const struct ast_event *event, void *data)
{
    struct ast_str *str;
    enum ast_security_event_type event_type;

    if (!(str = ast_str_thread_get(&security_event_buf,
                                   SECURITY_EVENT_BUF_INIT_LEN))) {
        return;
    }

    /* Note that the event type is guaranteed to be valid here. */
    event_type = ast_event_get_ie_uint(event, AST_EVENT_IE_SECURITY_EVENT);
    ast_assert(event_type >= 0 && event_type < AST_SECURITY_EVENT_NUM_TYPES);

    ast_str_set(&str, 0, "%s=\"%s\"",
                ast_event_get_ie_type_name(AST_EVENT_IE_SECURITY_EVENT),
                ast_security_event_get_name(event_type));

    append_ies(&str, event,
               ast_security_event_get_required_ies(event_type), REQUIRED);
    append_ies(&str, event,
               ast_security_event_get_optional_ies(event_type), NOT_REQUIRED);

    ast_log_dynamic_level(LOG_SECURITY, "%s\n", ast_str_buffer(str));
}
コード例 #4
0
/*!
 * \internal
 * \brief Build the NOTIFY request adding content or header info.
 */
static void build_notify(pjsip_tx_data *tdata, const char *name, const char *value,
			 struct ast_str **content_type, struct ast_str **content)
{
	if (not_allowed(name)) {
		ast_log(LOG_WARNING, "Cannot specify %s header, "
			"ignoring\n", name);
		return;
	}

	if (!strcasecmp(name, "Content-type")) {
		if (!(*content_type)) {
			*content_type = ast_str_create(CONTENT_TYPE_SIZE);
		}
		ast_str_set(content_type, 0,"%s", value);
	} else if (!strcasecmp(name, "Content")) {
		if (!(*content)) {
			*content = ast_str_create(CONTENT_SIZE);
		}

		if (ast_str_strlen(*content)) {
			ast_str_append(content, 0, "\r\n");
		}
		ast_str_append(content, 0, "%s", value);
	} else {
		ast_sip_add_header(tdata, name, value);
	}
}
コード例 #5
0
ファイル: stasis_channels.c プロジェクト: jcollie/asterisk
static struct ast_manager_event_blob *varset_to_ami(struct stasis_message *msg)
{
	RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
	struct ast_channel_blob *obj = stasis_message_data(msg);
	const char *variable =
		ast_json_string_get(ast_json_object_get(obj->blob, "variable"));
	const char *value =
		ast_json_string_get(ast_json_object_get(obj->blob, "value"));

	if (obj->snapshot) {
		channel_event_string =
			ast_manager_build_channel_state_string(obj->snapshot);
	} else {
		channel_event_string = ast_str_create(35);
		ast_str_set(&channel_event_string, 0,
			    "Channel: none\r\n"
			    "Uniqueid: none\r\n");
	}

	if (!channel_event_string) {
		return NULL;
	}

	return ast_manager_event_blob_create(EVENT_FLAG_DIALPLAN, "VarSet",
		"%s"
		"Variable: %s\r\n"
		"Value: %s\r\n",
		ast_str_buffer(channel_event_string), variable, value);
}
コード例 #6
0
ファイル: security_events.c プロジェクト: Evangileon/asterisk
static int add_ipv4_ie(struct ast_event **event, enum ast_event_ie_type ie_type,
		const struct ast_security_event_ipv4_addr *addr)
{
	struct ast_str *str = ast_str_alloca(64);

	ast_str_set(&str, 0, "IPV4/");

	switch (addr->transport) {
	case AST_SECURITY_EVENT_TRANSPORT_UDP:
		ast_str_append(&str, 0, "UDP/");
		break;
	case AST_SECURITY_EVENT_TRANSPORT_TCP:
		ast_str_append(&str, 0, "TCP/");
		break;
	case AST_SECURITY_EVENT_TRANSPORT_TLS:
		ast_str_append(&str, 0, "TLS/");
		break;
	}

	ast_str_append(&str, 0, "%s/%hu",
			ast_inet_ntoa(addr->sin->sin_addr),
			ntohs(addr->sin->sin_port));

	return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str));
}
コード例 #7
0
static struct ast_manager_event_blob *system_registry_to_ami(struct stasis_message *message)
{
    struct ast_json_payload *payload = stasis_message_data(message);
    const char *channeltype;
    const char *username;
    const char *domain;
    const char *status;
    const char *cause;
    RAII_VAR(struct ast_str *, cause_string, ast_str_create(32), ast_free);

    if (!cause_string) {
        return NULL;
    }

    channeltype = ast_json_string_get(ast_json_object_get(payload->json, "channeltype"));
    username = ast_json_string_get(ast_json_object_get(payload->json, "username"));
    domain = ast_json_string_get(ast_json_object_get(payload->json, "domain"));
    status = ast_json_string_get(ast_json_object_get(payload->json, "status"));
    cause = ast_json_string_get(ast_json_object_get(payload->json, "cause"));

    if (!ast_strlen_zero(cause)) {
        ast_str_set(&cause_string, 0, "Cause: %s\r\n", cause);
    }

    return ast_manager_event_blob_create(EVENT_FLAG_SYSTEM, "Registry",
                                         "ChannelType: %s\r\n"
                                         "Username: %s\r\n"
                                         "Domain: %s\r\n"
                                         "Status: %s\r\n"
                                         "%s",
                                         channeltype, username, domain, status, ast_str_buffer(cause_string));
}
コード例 #8
0
ファイル: res_security_log.c プロジェクト: adaptiman/asterisk
static void security_event_stasis_cb(struct ast_json *json)
{
	struct ast_str *str;
	struct ast_json *event_type_json;
	enum ast_security_event_type event_type;

	event_type_json = ast_json_object_get(json, "SecurityEvent");
	event_type = ast_json_integer_get(event_type_json);

	ast_assert(event_type >= 0 && event_type < AST_SECURITY_EVENT_NUM_TYPES);

	if (!(str = ast_str_thread_get(&security_event_buf,
			SECURITY_EVENT_BUF_INIT_LEN))) {
		return;
	}

	ast_str_set(&str, 0, "SecurityEvent=\"%s\"",
			ast_security_event_get_name(event_type));

	append_json(&str, json,
			ast_security_event_get_required_ies(event_type), REQUIRED);
	append_json(&str, json,
			ast_security_event_get_optional_ies(event_type), NOT_REQUIRED);

	ast_log_dynamic_level(LOG_SECURITY, "%s\n", ast_str_buffer(str));
}
コード例 #9
0
ファイル: cdr_mongodb.c プロジェクト: sashich/cdr_mongodb
static int load_config_string(struct ast_config *cfg, const char *category, const char *variable, struct ast_str **field, const char *def)
{
	struct unload_string *us;
	const char *tmp;

	if (!(us = ast_calloc(1, sizeof(*us)))) {
		return -1;
	}

	if (!(*field = ast_str_create(16))) {
		ast_free(us);
		return -1;
	}

	tmp = ast_variable_retrieve(cfg, category, variable);

	ast_str_set(field, 0, "%s", tmp ? tmp : def);

	us->str = *field;

	AST_LIST_LOCK(&unload_strings);
	AST_LIST_INSERT_HEAD(&unload_strings, us, entry);
	AST_LIST_UNLOCK(&unload_strings);

	return 0;
}
コード例 #10
0
struct ast_str *ast_manager_build_bridge_state_string_prefix(
	const struct ast_bridge_snapshot *snapshot,
	const char *prefix)
{
	struct ast_str *out = ast_str_create(128);
	int res;

	if (!out) {
		return NULL;
	}

	res = ast_str_set(&out, 0,
		"%sBridgeUniqueid: %s\r\n"
		"%sBridgeType: %s\r\n"
		"%sBridgeTechnology: %s\r\n"
		"%sBridgeCreator: %s\r\n"
		"%sBridgeName: %s\r\n"
		"%sBridgeNumChannels: %u\r\n",
		prefix, snapshot->uniqueid,
		prefix, snapshot->subclass,
		prefix, snapshot->technology,
		prefix, ast_strlen_zero(snapshot->creator) ? "<unknown>": snapshot->creator,
		prefix, ast_strlen_zero(snapshot->name) ? "<unknown>": snapshot->name,
		prefix, snapshot->num_channels);
	if (!res) {
		ast_free(out);
		return NULL;
	}

	return out;
}
コード例 #11
0
static enum ast_test_result_state test_chan_variable(struct ast_test *test,
		struct ast_channel *c, const char *varname)
{
	const char *values[] = { "one", "three", "reallylongdinosaursoundingthingwithwordsinit" };
	int i, okay = 1;
	char workspace[4096];
	struct ast_str *str = ast_str_create(16);
	struct ast_str *var = ast_str_create(16);

	ast_str_set(&var, 0, "${%s}", varname);
	for (i = 0; i < ARRAY_LEN(values); i++) {
		pbx_builtin_setvar_helper(c, varname, values[i]);
		ast_str_substitute_variables(&str, 0, c, ast_str_buffer(var));
		pbx_substitute_variables_helper(c, ast_str_buffer(var), workspace, sizeof(workspace));
		ast_test_status_update(test, "Testing '%s' . . . . . %s\n",
				ast_str_buffer(var), okay ? "passed" : "FAILED");
		if (strcmp(values[i], ast_str_buffer(str)) != 0 || strcmp(values[i], workspace) != 0) {
			ast_test_status_update(test, "%s != %s != %s\n",
					values[i], ast_str_buffer(str), workspace);
			okay = 0;
		}
	}

	ast_free(str);
	ast_free(var);

	return okay ? AST_TEST_PASS : AST_TEST_FAIL;
}
コード例 #12
0
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;
}
コード例 #13
0
ファイル: res_pjsip_sdp_rtp.c プロジェクト: popsodav/asterisk
/*! \brief Function which adds ICE attributes to a media stream */
static void add_ice_to_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)
{
	struct ast_rtp_engine_ice *ice;
	struct ao2_container *candidates;
	const char *username, *password;
	pj_str_t stmp;
	pjmedia_sdp_attr *attr;
	struct ao2_iterator it_candidates;
	struct ast_rtp_engine_ice_candidate *candidate;

	if (!session->endpoint->media.rtp.ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp)) ||
		!(candidates = ice->get_local_candidates(session_media->rtp))) {
		return;
	}

	if ((username = ice->get_ufrag(session_media->rtp))) {
		attr = pjmedia_sdp_attr_create(pool, "ice-ufrag", pj_cstr(&stmp, username));
		media->attr[media->attr_count++] = attr;
	}

	if ((password = ice->get_password(session_media->rtp))) {
		attr = pjmedia_sdp_attr_create(pool, "ice-pwd", pj_cstr(&stmp, password));
		media->attr[media->attr_count++] = attr;
	}

	it_candidates = ao2_iterator_init(candidates, 0);
	for (; (candidate = ao2_iterator_next(&it_candidates)); ao2_ref(candidate, -1)) {
		struct ast_str *attr_candidate = ast_str_create(128);

		ast_str_set(&attr_candidate, -1, "%s %u %s %d %s ", candidate->foundation, candidate->id, candidate->transport,
					candidate->priority, ast_sockaddr_stringify_addr_remote(&candidate->address));
		ast_str_append(&attr_candidate, -1, "%s typ ", ast_sockaddr_stringify_port(&candidate->address));

		switch (candidate->type) {
			case AST_RTP_ICE_CANDIDATE_TYPE_HOST:
				ast_str_append(&attr_candidate, -1, "host");
				break;
			case AST_RTP_ICE_CANDIDATE_TYPE_SRFLX:
				ast_str_append(&attr_candidate, -1, "srflx");
				break;
			case AST_RTP_ICE_CANDIDATE_TYPE_RELAYED:
				ast_str_append(&attr_candidate, -1, "relay");
				break;
		}

		if (!ast_sockaddr_isnull(&candidate->relay_address)) {
			ast_str_append(&attr_candidate, -1, " raddr %s rport", ast_sockaddr_stringify_addr_remote(&candidate->relay_address));
			ast_str_append(&attr_candidate, -1, " %s", ast_sockaddr_stringify_port(&candidate->relay_address));
		}

		attr = pjmedia_sdp_attr_create(pool, "candidate", pj_cstr(&stmp, ast_str_buffer(attr_candidate)));
		media->attr[media->attr_count++] = attr;

		ast_free(attr_candidate);
	}

	ao2_iterator_destroy(&it_candidates);
	ao2_ref(candidates, -1);
}
コード例 #14
0
ファイル: res_http_post.c プロジェクト: n02018222/asterisk
static int __ast_http_post_load(int reload)
{
	struct ast_config *cfg;
	struct ast_variable *v;
	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };

	cfg = ast_config_load2("http.conf", "http", config_flags);
	if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
		return 0;
	}

	if (reload) {
		ast_http_uri_unlink_all_with_key(__FILE__);
	}

	if (cfg) {
		for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
			if (!strcasecmp(v->name, "prefix")) {
				ast_copy_string(prefix, v->value, sizeof(prefix));
				if (prefix[strlen(prefix)] == '/') {
					prefix[strlen(prefix)] = '\0';
				}
			}
		}

		for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next) {
			struct ast_http_uri *urih;
			struct ast_str *ds;

			if (!(urih = ast_calloc(sizeof(*urih), 1))) {
				ast_config_destroy(cfg);
				return -1;
			}

			if (!(ds = ast_str_create(32))) {
				ast_free(urih);
				ast_config_destroy(cfg);
				return -1;
			}

			urih->description = ast_strdup("HTTP POST mapping");
			urih->uri = ast_strdup(v->name);
			ast_str_set(&ds, 0, "%s", v->value);
			urih->data = ds;
			urih->has_subtree = 0;
			urih->supports_get = 0;
			urih->supports_post = 1;
			urih->callback = http_post_callback;
			urih->key = __FILE__;
			urih->mallocd = urih->dmallocd = 1;

			ast_http_uri_link(urih);
		}

		ast_config_destroy(cfg);
	}
	return 0;
}
コード例 #15
0
/*!
 * \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;
}
コード例 #16
0
static int manager_bridge_info(struct mansession *s, const struct message *m)
{
	const char *id = astman_get_header(m, "ActionID");
	const char *bridge_uniqueid = astman_get_header(m, "BridgeUniqueid");
	RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
	RAII_VAR(struct ast_str *, bridge_info, NULL, ast_free);
	struct ast_bridge_snapshot *snapshot;
	struct bridge_list_data list_data;

	if (!id_text) {
		astman_send_error(s, m, "Internal error");
		return -1;
	}

	if (ast_strlen_zero(bridge_uniqueid)) {
		astman_send_error(s, m, "BridgeUniqueid must be provided");
		return 0;
	}

	if (!ast_strlen_zero(id)) {
		ast_str_set(&id_text, 0, "ActionID: %s\r\n", id);
	}

	msg = stasis_cache_get(ast_bridge_cache(), ast_bridge_snapshot_type(), bridge_uniqueid);
	if (!msg) {
		astman_send_error(s, m, "Specified BridgeUniqueid not found");
		return 0;
	}

	snapshot = stasis_message_data(msg);
	bridge_info = ast_manager_build_bridge_state_string(snapshot);
	if (!bridge_info) {
		astman_send_error(s, m, "Internal error");
		return -1;
	}

	astman_send_listack(s, m, "Bridge channel listing will follow", "start");

	list_data.id_text = ast_str_buffer(id_text);
	list_data.count = 0;
	ao2_callback_data(snapshot->channels, OBJ_NODATA, send_bridge_info_item_cb, s, &list_data);

	astman_send_list_complete_start(s, m, "BridgeInfoComplete", list_data.count);
	if (!ast_strlen_zero(ast_str_buffer(bridge_info))) {
		astman_append(s, "%s", ast_str_buffer(bridge_info));
	}
	astman_send_list_complete_end(s);

	return 0;
}
コード例 #17
0
static void *transport_websocket_init(const struct ast_socket_io_session *session)
{
	struct ast_websocket *ws;
	struct ast_str *uri = ast_str_create(128);
	enum ast_websocket_result result;
	struct ast_uri *session_uri = ast_socket_io_uri(session);
	struct ast_tls_config *tls_cfg = NULL;

	if (!uri) {
		ast_log(LOG_ERROR, "Unable to allocate websocket uri\n");
		return NULL;
	}

	ast_str_set(&uri, 0, "%s://%s:%s/socket.io/%s/websocket/%s",
		    ast_uri_is_secure(session_uri) ? "wss" : "ws",
		    ast_uri_host(session_uri), ast_uri_port(session_uri),
		    SOCKET_IO_VERSION, ast_socket_io_id(session));

	if (!ast_strlen_zero(ast_uri_query(session_uri))) {
		ast_str_append(&uri, 0, "?%s", ast_uri_query(session_uri));
	}

	if (ast_uri_is_secure(session_uri)) {
		/* The websocket client expects a copy of the TLS configuration so provide it */
		tls_cfg = ast_calloc(1, sizeof(*tls_cfg));
		ast_set_flag(&tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER);
	}

	ws = ast_websocket_client_create(
		ast_str_buffer(uri), "socket.io",
			tls_cfg, &result);
	ast_free(uri);

	if (ws) {
		struct protoent *p;

		p = getprotobyname("tcp");
		if (p) {
			int arg = 1;

			if (setsockopt(ast_websocket_fd(ws), p->p_proto, TCP_NODELAY, (char *) &arg, sizeof(arg) ) < 0) {
				ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on HTTP connection: %s\n", strerror(errno));
				ast_log(LOG_WARNING, "Some HTTP requests may be slow to respond.\n");
			}
		}

		ast_websocket_set_nonblock(ws);
	}

	return ws;
}
コード例 #18
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));
}
コード例 #19
0
ファイル: res_pjsip_path.c プロジェクト: auntieNeo/asterisk
static struct ast_sip_aor *find_aor(struct ast_sip_endpoint *endpoint, pjsip_uri *uri)
{
	char *configured_aors, *aor_name;
	pjsip_sip_uri *sip_uri;
	char *domain_name;
	RAII_VAR(struct ast_str *, id, NULL, ast_free);

	if (ast_strlen_zero(endpoint->aors)) {
		return NULL;
	}

	sip_uri = pjsip_uri_get_uri(uri);
	domain_name = ast_alloca(sip_uri->host.slen + 1);
	ast_copy_pj_str(domain_name, &sip_uri->host, sip_uri->host.slen + 1);

	configured_aors = ast_strdupa(endpoint->aors);

	/* Iterate the configured AORs to see if the user or the user+domain match */
	while ((aor_name = strsep(&configured_aors, ","))) {
		struct ast_sip_domain_alias *alias = NULL;

		if (!pj_strcmp2(&sip_uri->user, aor_name)) {
			break;
		}

		if (!id && !(id = ast_str_create(sip_uri->user.slen + sip_uri->host.slen + 2))) {
			return NULL;
		}

		ast_str_set(&id, 0, "%.*s@", (int)sip_uri->user.slen, sip_uri->user.ptr);
		if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) {
			ast_str_append(&id, 0, "%s", alias->domain);
			ao2_cleanup(alias);
		} else {
			ast_str_append(&id, 0, "%s", domain_name);
		}

		if (!strcmp(aor_name, ast_str_buffer(id))) {
			ast_free(id);
			break;
		}
	}

	if (ast_strlen_zero(aor_name)) {
		return NULL;
	}

	return ast_sip_location_retrieve_aor(aor_name);
}
コード例 #20
0
/*!
 * \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;
}
コード例 #21
0
static int pgsql_reconnect(const char *database)
{
	char my_database[50];

	ast_copy_string(my_database, S_OR(database, dbname), sizeof(my_database));

	/* mutex lock should have been locked before calling this function. */

	if (pgsqlConn && PQstatus(pgsqlConn) != CONNECTION_OK) {
		PQfinish(pgsqlConn);
		pgsqlConn = NULL;
	}

	/* DB password can legitimately be 0-length */
	if ((!pgsqlConn) && (!ast_strlen_zero(dbhost) || !ast_strlen_zero(dbsock)) && !ast_strlen_zero(dbuser) && !ast_strlen_zero(my_database)) {
		struct ast_str *connInfo = ast_str_create(32);

		ast_str_set(&connInfo, 0, "host=%s port=%d dbname=%s user=%s",
			dbhost, dbport, my_database, dbuser);
		if (!ast_strlen_zero(dbpass))
			ast_str_append(&connInfo, 0, " password=%s", dbpass);

		ast_debug(1, "%u connInfo=%s\n", (unsigned int)connInfo->len, connInfo->str);
		pgsqlConn = PQconnectdb(connInfo->str);
		ast_debug(1, "%u connInfo=%s\n", (unsigned int)connInfo->len, connInfo->str);
		ast_free(connInfo);
		connInfo = NULL;

		ast_debug(1, "pgsqlConn=%p\n", pgsqlConn);
		if (pgsqlConn && PQstatus(pgsqlConn) == CONNECTION_OK) {
			ast_debug(1, "PostgreSQL RealTime: Successfully connected to database.\n");
			connect_time = time(NULL);
			return 1;
		} else {
			ast_log(LOG_ERROR,
					"PostgreSQL RealTime: Failed to connect database %s on %s: %s\n",
					dbname, dbhost, PQresultErrorMessage(NULL));
			return 0;
		}
	} else {
		ast_debug(1, "PostgreSQL RealTime: One or more of the parameters in the config does not pass our validity checks.\n");
		return 1;
	}
}
コード例 #22
0
ファイル: parking_manager.c プロジェクト: lyx2014/Asterisk
/*! \brief Builds a manager string based on the contents of a parked call payload */
static struct ast_str *manager_build_parked_call_string(const struct ast_parked_call_payload *payload)
{
	struct ast_str *out = ast_str_create(1024);
	RAII_VAR(struct ast_str *, parkee_string, NULL, ast_free);
	RAII_VAR(struct ast_str *, retriever_string, NULL, ast_free);

	if (!out) {
		return NULL;
	}

	parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
	if (!parkee_string) {
		ast_free(out);
		return NULL;
	}

	if (payload->retriever) {
		retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
		if (!retriever_string) {
			ast_free(out);
			return NULL;
		}
	}

	ast_str_set(&out, 0,
		"%s" /* parkee channel state */
		"%s" /* retriever channel state (when available) */
		"ParkerDialString: %s\r\n"
		"Parkinglot: %s\r\n"
		"ParkingSpace: %u\r\n"
		"ParkingTimeout: %lu\r\n"
		"ParkingDuration: %lu\r\n",

		ast_str_buffer(parkee_string),
		retriever_string ? ast_str_buffer(retriever_string) : "",
		payload->parker_dial_string,
		payload->parkinglot,
		payload->parkingspace,
		payload->timeout,
		payload->duration);

	return out;
}
コード例 #23
0
struct ast_str *ast_manager_build_bridge_state_string_prefix(
	const struct ast_bridge_snapshot *snapshot,
	const char *prefix)
{
	struct ast_str *out = ast_str_create(128);
	int res;

	if (!out) {
		return NULL;
	}

	res = ast_str_set(&out, 0,
		"%sBridgeUniqueid: %s\r\n"
		"%sBridgeType: %s\r\n"
		"%sBridgeTechnology: %s\r\n"
		"%sBridgeCreator: %s\r\n"
		"%sBridgeName: %s\r\n"
		"%sBridgeNumChannels: %u\r\n"
		"%sBridgeVideoSourceMode: %s\r\n",
		prefix, snapshot->uniqueid,
		prefix, snapshot->subclass,
		prefix, snapshot->technology,
		prefix, ast_strlen_zero(snapshot->creator) ? "<unknown>": snapshot->creator,
		prefix, ast_strlen_zero(snapshot->name) ? "<unknown>": snapshot->name,
		prefix, snapshot->num_channels,
		prefix, ast_bridge_video_mode_to_string(snapshot->video_mode));
	if (!res) {
		ast_free(out);
		return NULL;
	}

	if (snapshot->video_mode != AST_BRIDGE_VIDEO_MODE_NONE
		&& !ast_strlen_zero(snapshot->video_source_id)) {
		res = ast_str_append(&out, 0, "%sBridgeVideoSource: %s\r\n",
			prefix, snapshot->video_source_id);
		if (!res) {
			ast_free(out);
			return NULL;
		}
	}

	return out;
}
コード例 #24
0
ファイル: json.c プロジェクト: huangjingpei/asterisk
struct ast_json *ast_json_ipaddr(const struct ast_sockaddr *addr, enum ast_transport transport_type)
{
	struct ast_str *string = ast_str_alloca(64);

	if (!string) {
		return NULL;
	}

	ast_str_set(&string, 0, (ast_sockaddr_is_ipv4(addr) ||
		ast_sockaddr_is_ipv4_mapped(addr)) ? "IPV4/" : "IPV6/");

	if (transport_type) {
		char *transport_string = NULL;

		/* NOTE: None will be applied if multiple transport types are specified in transport_type */
		switch(transport_type) {
		case AST_TRANSPORT_UDP:
			transport_string = "UDP";
			break;
		case AST_TRANSPORT_TCP:
			transport_string = "TCP";
			break;
		case AST_TRANSPORT_TLS:
			transport_string = "TLS";
			break;
		case AST_TRANSPORT_WS:
			transport_string = "WS";
			break;
		case AST_TRANSPORT_WSS:
			transport_string = "WSS";
			break;
		}

		if (transport_string) {
			ast_str_append(&string, 0, "%s/", transport_string);
		}
	}

	ast_str_append(&string, 0, "%s", ast_sockaddr_stringify_addr(addr));
	ast_str_append(&string, 0, "/%s", ast_sockaddr_stringify_port(addr));

	return ast_json_string_create(ast_str_buffer(string));
}
コード例 #25
0
static int manager_bridges_list(struct mansession *s, const struct message *m)
{
	const char *id = astman_get_header(m, "ActionID");
	const char *type_filter = astman_get_header(m, "BridgeType");
	RAII_VAR(struct ast_str *, id_text, ast_str_create(128), ast_free);
	RAII_VAR(struct ao2_container *, bridges, NULL, ao2_cleanup);
	struct bridge_list_data list_data;

	if (!id_text) {
		astman_send_error(s, m, "Internal error");
		return -1;
	}

	if (!ast_strlen_zero(id)) {
		ast_str_set(&id_text, 0, "ActionID: %s\r\n", id);
	}

	bridges = stasis_cache_dump(ast_bridge_cache(), ast_bridge_snapshot_type());
	if (!bridges) {
		astman_send_error(s, m, "Internal error");
		return -1;
	}

	astman_send_listack(s, m, "Bridge listing will follow", "start");

	if (!ast_strlen_zero(type_filter)) {
		char *type_filter_dup = ast_strdupa(type_filter);

		ao2_callback(bridges, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK,
			filter_bridge_type_cb, type_filter_dup);
	}

	list_data.id_text = ast_str_buffer(id_text);
	list_data.count = 0;
	ao2_callback_data(bridges, OBJ_NODATA, send_bridge_list_item_cb, s, &list_data);

	astman_send_list_complete_start(s, m, "BridgeListComplete", list_data.count);
	astman_send_list_complete_end(s);

	return 0;
}
コード例 #26
0
static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)
{
	pjsip_generic_string_hdr *path_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &path_hdr_name, NULL);

	if (!path_hdr) {
		return 0;
	}

	*path_str = ast_str_create(64);
	if (!path_str) {
		return -1;
	}

	ast_str_set(path_str, 0, "%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);

	while ((path_hdr = (pjsip_generic_string_hdr *) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &path_hdr_name, path_hdr->next))) {
		ast_str_append(path_str, 0, ",%.*s", (int)path_hdr->hvalue.slen, path_hdr->hvalue.ptr);
	}

	return 0;
}
コード例 #27
0
ファイル: res_chan_stats.c プロジェクト: auntieNeo/asterisk
/*!
 * \brief Subscription callback for all channel messages.
 * \param data Data pointer given when creating the subscription.
 * \param sub This subscription.
 * \param topic The topic the message was posted to. This is not necessarily the
 *              topic you subscribed to, since messages may be forwarded between
 *              topics.
 * \param message The message itself.
 */
static void statsmaker(void *data, struct stasis_subscription *sub,
	struct stasis_message *message)
{
	RAII_VAR(struct ast_str *, metric, NULL, ast_free);

	if (stasis_subscription_final_message(sub, message)) {
		/* Normally, data points to an object that must be cleaned up.
		 * The final message is an unsubscribe notification that's
		 * guaranteed to be the last message this subscription receives.
		 * This would be a safe place to kick off any needed cleanup.
		 */
		return;
	}

	/* For no good reason, count message types */
	metric = ast_str_create(80);
	if (metric) {
		ast_str_set(&metric, 0, "stasis.message.%s",
			stasis_message_type_name(stasis_message_type(message)));
		ast_statsd_log(ast_str_buffer(metric), AST_STATSD_METER, 1);
	}
}
コード例 #28
0
ファイル: threadpool.c プロジェクト: GGGO/asterisk
/*
 * \brief Allocate a threadpool
 *
 * This is implemented as a taskprocessor listener's alloc callback. This
 * is because the threadpool exists as the private data on a taskprocessor
 * listener.
 *
 * \param name The name of the threadpool.
 * \param options The options the threadpool uses.
 * \retval NULL Could not initialize threadpool properly
 * \retval non-NULL The newly-allocated threadpool
 */
static struct ast_threadpool *threadpool_alloc(const char *name, const struct ast_threadpool_options *options)
{
	RAII_VAR(struct ast_threadpool *, pool, NULL, ao2_cleanup);
	struct ast_str *control_tps_name;

	pool = ao2_alloc(sizeof(*pool), threadpool_destructor);
	control_tps_name = ast_str_create(64);
	if (!pool || !control_tps_name) {
		ast_free(control_tps_name);
		return NULL;
	}

	ast_str_set(&control_tps_name, 0, "%s-control", name);

	pool->control_tps = ast_taskprocessor_get(ast_str_buffer(control_tps_name), TPS_REF_DEFAULT);
	ast_free(control_tps_name);
	if (!pool->control_tps) {
		return NULL;
	}
	pool->active_threads = ao2_container_alloc(THREAD_BUCKETS, worker_thread_hash, worker_thread_cmp);
	if (!pool->active_threads) {
		return NULL;
	}
	pool->idle_threads = ao2_container_alloc(THREAD_BUCKETS, worker_thread_hash, worker_thread_cmp);
	if (!pool->idle_threads) {
		return NULL;
	}
	pool->zombie_threads = ao2_container_alloc(THREAD_BUCKETS, worker_thread_hash, worker_thread_cmp);
	if (!pool->zombie_threads) {
		return NULL;
	}
	pool->options = *options;

	ao2_ref(pool, +1);
	return pool;
}
コード例 #29
0
ファイル: res_pjsip_sdp_rtp.c プロジェクト: popsodav/asterisk
static int add_crypto_to_stream(struct ast_sip_session *session,
	struct ast_sip_session_media *session_media,
	pj_pool_t *pool, pjmedia_sdp_media *media)
{
	pj_str_t stmp;
	pjmedia_sdp_attr *attr;
	enum ast_rtp_dtls_hash hash;
	const char *crypto_attribute;
	struct ast_rtp_engine_dtls *dtls;
	static const pj_str_t STR_NEW = { "new", 3 };
	static const pj_str_t STR_EXISTING = { "existing", 8 };
	static const pj_str_t STR_ACTIVE = { "active", 6 };
	static const pj_str_t STR_PASSIVE = { "passive", 7 };
	static const pj_str_t STR_ACTPASS = { "actpass", 7 };
	static const pj_str_t STR_HOLDCONN = { "holdconn", 8 };

	switch (session_media->encryption) {
	case AST_SIP_MEDIA_ENCRYPT_NONE:
	case AST_SIP_MEDIA_TRANSPORT_INVALID:
		break;
	case AST_SIP_MEDIA_ENCRYPT_SDES:
		if (!session_media->srtp) {
			session_media->srtp = ast_sdp_srtp_alloc();
			if (!session_media->srtp) {
				return -1;
			}
		}

		crypto_attribute = ast_sdp_srtp_get_attrib(session_media->srtp,
			0 /* DTLS running? No */,
			session->endpoint->media.rtp.srtp_tag_32 /* 32 byte tag length? */);
		if (!crypto_attribute) {
			/* No crypto attribute to add, bad news */
			return -1;
		}

		attr = pjmedia_sdp_attr_create(pool, "crypto", pj_cstr(&stmp, crypto_attribute));
		media->attr[media->attr_count++] = attr;
		break;
	case AST_SIP_MEDIA_ENCRYPT_DTLS:
		if (setup_dtls_srtp(session, session_media)) {
			return -1;
		}

		dtls = ast_rtp_instance_get_dtls(session_media->rtp);
		if (!dtls) {
			return -1;
		}

		switch (dtls->get_connection(session_media->rtp)) {
		case AST_RTP_DTLS_CONNECTION_NEW:
			attr = pjmedia_sdp_attr_create(pool, "connection", &STR_NEW);
			media->attr[media->attr_count++] = attr;
			break;
		case AST_RTP_DTLS_CONNECTION_EXISTING:
			attr = pjmedia_sdp_attr_create(pool, "connection", &STR_EXISTING);
			media->attr[media->attr_count++] = attr;
			break;
		default:
			break;
		}

		switch (dtls->get_setup(session_media->rtp)) {
		case AST_RTP_DTLS_SETUP_ACTIVE:
			attr = pjmedia_sdp_attr_create(pool, "setup", &STR_ACTIVE);
			media->attr[media->attr_count++] = attr;
			break;
		case AST_RTP_DTLS_SETUP_PASSIVE:
			attr = pjmedia_sdp_attr_create(pool, "setup", &STR_PASSIVE);
			media->attr[media->attr_count++] = attr;
			break;
		case AST_RTP_DTLS_SETUP_ACTPASS:
			attr = pjmedia_sdp_attr_create(pool, "setup", &STR_ACTPASS);
			media->attr[media->attr_count++] = attr;
			break;
		case AST_RTP_DTLS_SETUP_HOLDCONN:
			attr = pjmedia_sdp_attr_create(pool, "setup", &STR_HOLDCONN);
			media->attr[media->attr_count++] = attr;
			break;
		default:
			break;
		}

		hash = dtls->get_fingerprint_hash(session_media->rtp);
		crypto_attribute = dtls->get_fingerprint(session_media->rtp);
		if (crypto_attribute && (hash == AST_RTP_DTLS_HASH_SHA1 || hash == AST_RTP_DTLS_HASH_SHA256)) {
			RAII_VAR(struct ast_str *, fingerprint, ast_str_create(64), ast_free);
			if (!fingerprint) {
				return -1;
			}

			if (hash == AST_RTP_DTLS_HASH_SHA1) {
				ast_str_set(&fingerprint, 0, "SHA-1 %s", crypto_attribute);
			} else {
				ast_str_set(&fingerprint, 0, "SHA-256 %s", crypto_attribute);
			}

			attr = pjmedia_sdp_attr_create(pool, "fingerprint", pj_cstr(&stmp, ast_str_buffer(fingerprint)));
			media->attr[media->attr_count++] = attr;
		}
		break;
	}
コード例 #30
0
/*!
 * \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;
}