mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	This change adds support for refers that are not session based. It includes a refer implementation for the PJSIP technology which results in out-of-dialog REFERs being sent to a PJSIP endpoint. These can be triggered using the new ARI endpoint `/endpoints/refer`. Resolves: #71 UserNote: There is a new ARI endpoint `/endpoints/refer` for referring an endpoint to some URI or endpoint.
		
			
				
	
	
		
			443 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			443 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Asterisk -- An open source telephony toolkit.
 | |
|  *
 | |
|  * Copyright (C) 2012 - 2013, Digium, Inc.
 | |
|  *
 | |
|  * David M. Lee, II <dlee@digium.com>
 | |
|  *
 | |
|  * See http://www.asterisk.org for more information about
 | |
|  * the Asterisk project. Please do not directly contact
 | |
|  * any of the maintainers of this project for assistance;
 | |
|  * the project provides a web site, mailing lists and IRC
 | |
|  * channels for your use.
 | |
|  *
 | |
|  * This program is free software, distributed under the terms of
 | |
|  * the GNU General Public License Version 2. See the LICENSE file
 | |
|  * at the top of the source tree.
 | |
|  */
 | |
| 
 | |
| /*! \file
 | |
|  *
 | |
|  * \brief /api-docs/endpoints.{format} implementation- Endpoint resources
 | |
|  *
 | |
|  * \author David M. Lee, II <dlee@digium.com>
 | |
|  */
 | |
| 
 | |
| #include "asterisk.h"
 | |
| 
 | |
| #include "resource_endpoints.h"
 | |
| 
 | |
| #include "asterisk/astobj2.h"
 | |
| #include "asterisk/stasis.h"
 | |
| #include "asterisk/stasis_app.h"
 | |
| #include "asterisk/stasis_endpoints.h"
 | |
| #include "asterisk/channel.h"
 | |
| #include "asterisk/message.h"
 | |
| #include "asterisk/refer.h"
 | |
| 
 | |
| void ast_ari_endpoints_list(struct ast_variable *headers,
 | |
| 	struct ast_ari_endpoints_list_args *args,
 | |
| 	struct ast_ari_response *response)
 | |
| {
 | |
| 	RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);
 | |
| 	RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);
 | |
| 	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
 | |
| 	struct ao2_iterator i;
 | |
| 	void *obj;
 | |
| 
 | |
| 	cache = ast_endpoint_cache();
 | |
| 	if (!cache) {
 | |
| 		ast_ari_response_error(
 | |
| 			response, 500, "Internal Server Error",
 | |
| 			"Message bus not initialized");
 | |
| 		return;
 | |
| 	}
 | |
| 	ao2_ref(cache, +1);
 | |
| 
 | |
| 	snapshots = stasis_cache_dump(cache, ast_endpoint_snapshot_type());
 | |
| 	if (!snapshots) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	json = ast_json_array_create();
 | |
| 	if (!json) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	i = ao2_iterator_init(snapshots, 0);
 | |
| 	while ((obj = ao2_iterator_next(&i))) {
 | |
| 		RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
 | |
| 		struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg);
 | |
| 		struct ast_json *json_endpoint = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer());
 | |
| 
 | |
| 		if (!json_endpoint || ast_json_array_append(json, json_endpoint)) {
 | |
| 			ao2_iterator_destroy(&i);
 | |
| 			ast_ari_response_alloc_failed(response);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 	ao2_iterator_destroy(&i);
 | |
| 
 | |
| 	ast_ari_response_ok(response, ast_json_ref(json));
 | |
| }
 | |
| 
 | |
| void ast_ari_endpoints_list_by_tech(struct ast_variable *headers,
 | |
| 	struct ast_ari_endpoints_list_by_tech_args *args,
 | |
| 	struct ast_ari_response *response)
 | |
| {
 | |
| 	RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);
 | |
| 	RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);
 | |
| 	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
 | |
| 	struct ast_endpoint *tech_endpoint;
 | |
| 	struct ao2_iterator i;
 | |
| 	void *obj;
 | |
| 
 | |
| 	tech_endpoint = ast_endpoint_find_by_id(args->tech);
 | |
| 	if (!tech_endpoint) {
 | |
| 		ast_ari_response_error(response, 404, "Not Found",
 | |
| 				       "No Endpoints found - invalid tech %s", args->tech);
 | |
| 		return;
 | |
| 	}
 | |
| 	ao2_ref(tech_endpoint, -1);
 | |
| 
 | |
| 	cache = ast_endpoint_cache();
 | |
| 	if (!cache) {
 | |
| 		ast_ari_response_error(
 | |
| 			response, 500, "Internal Server Error",
 | |
| 			"Message bus not initialized");
 | |
| 		return;
 | |
| 	}
 | |
| 	ao2_ref(cache, +1);
 | |
| 
 | |
| 	snapshots = stasis_cache_dump(cache, ast_endpoint_snapshot_type());
 | |
| 	if (!snapshots) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	json = ast_json_array_create();
 | |
| 	if (!json) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	i = ao2_iterator_init(snapshots, 0);
 | |
| 	while ((obj = ao2_iterator_next(&i))) {
 | |
| 		RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
 | |
| 		struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg);
 | |
| 		struct ast_json *json_endpoint;
 | |
| 		int r;
 | |
| 
 | |
| 		if (strcasecmp(args->tech, snapshot->tech) != 0) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		json_endpoint = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer());
 | |
| 		if (!json_endpoint) {
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		r = ast_json_array_append(
 | |
| 			json, json_endpoint);
 | |
| 		if (r != 0) {
 | |
| 			ao2_iterator_destroy(&i);
 | |
| 			ast_ari_response_alloc_failed(response);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 	ao2_iterator_destroy(&i);
 | |
| 	ast_ari_response_ok(response, ast_json_ref(json));
 | |
| }
 | |
| 
 | |
| void ast_ari_endpoints_get(struct ast_variable *headers,
 | |
| 	struct ast_ari_endpoints_get_args *args,
 | |
| 	struct ast_ari_response *response)
 | |
| {
 | |
| 	struct ast_json *json;
 | |
| 	RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup);
 | |
| 
 | |
| 	snapshot = ast_endpoint_latest_snapshot(args->tech, args->resource);
 | |
| 	if (!snapshot) {
 | |
| 		ast_ari_response_error(response, 404, "Not Found",
 | |
| 			"Endpoint not found");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	json = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer());
 | |
| 	if (!json) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ast_ari_response_ok(response, json);
 | |
| }
 | |
| 
 | |
| static void send_message(const char *to, const char *from, const char *body, struct ast_variable *variables, struct ast_ari_response *response)
 | |
| {
 | |
| 	struct ast_variable *current;
 | |
| 	struct ast_msg *msg;
 | |
| 	int res = 0;
 | |
| 
 | |
| 	if (ast_strlen_zero(to)) {
 | |
| 		ast_ari_response_error(response, 400, "Bad Request",
 | |
| 			"To must be specified");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	msg = ast_msg_alloc();
 | |
| 	if (!msg) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	res |= ast_msg_set_from(msg, "%s", from);
 | |
| 	res |= ast_msg_set_to(msg, "%s", to);
 | |
| 
 | |
| 	if (!ast_strlen_zero(body)) {
 | |
| 		res |= ast_msg_set_body(msg, "%s", body);
 | |
| 	}
 | |
| 
 | |
| 	for (current = variables; current; current = current->next) {
 | |
| 		res |= ast_msg_set_var_outbound(msg, current->name, current->value);
 | |
| 	}
 | |
| 
 | |
| 	if (res) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		ast_msg_destroy(msg);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (ast_msg_send(msg, to, from)) {
 | |
| 		ast_ari_response_error(response, 404, "Not Found",
 | |
| 			"Endpoint not found");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	response->message = ast_json_null();
 | |
| 	response->response_code = 202;
 | |
| 	response->response_text = "Accepted";
 | |
| }
 | |
| 
 | |
| /*!
 | |
|  * \internal
 | |
|  * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
 | |
|  * \since 13.3.0
 | |
|  *
 | |
|  * \param[out] response HTTP response if error
 | |
|  * \param json_variables The JSON blob containing the variable
 | |
|  * \param[out] variables An out reference to the variables to populate.
 | |
|  *
 | |
|  * \retval 0 on success.
 | |
|  * \retval -1 on error.
 | |
|  */
 | |
| static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
 | |
| {
 | |
| 	enum ast_json_to_ast_vars_code res;
 | |
| 
 | |
| 	res = ast_json_to_ast_variables(json_variables, variables);
 | |
| 	switch (res) {
 | |
| 	case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
 | |
| 		return 0;
 | |
| 	case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
 | |
| 		ast_ari_response_error(response, 400, "Bad Request",
 | |
| 			"Only string values in the 'variables' object allowed");
 | |
| 		break;
 | |
| 	case AST_JSON_TO_AST_VARS_CODE_OOM:
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		break;
 | |
| 	}
 | |
| 	ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| void ast_ari_endpoints_send_message(struct ast_variable *headers,
 | |
| 	struct ast_ari_endpoints_send_message_args *args,
 | |
| 	struct ast_ari_response *response)
 | |
| {
 | |
| 	struct ast_variable *variables = NULL;
 | |
| 
 | |
| 	if (args->variables) {
 | |
| 		struct ast_json *json_variables;
 | |
| 
 | |
| 		ast_ari_endpoints_send_message_parse_body(args->variables, args);
 | |
| 		json_variables = ast_json_object_get(args->variables, "variables");
 | |
| 		if (json_variables
 | |
| 			&& json_to_ast_variables(response, json_variables, &variables)) {
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	send_message(args->to, args->from, args->body, variables, response);
 | |
| 	ast_variables_destroy(variables);
 | |
| }
 | |
| 
 | |
| void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers,
 | |
| 	struct ast_ari_endpoints_send_message_to_endpoint_args *args,
 | |
| 	struct ast_ari_response *response)
 | |
| {
 | |
| 	struct ast_variable *variables = NULL;
 | |
| 	struct ast_endpoint_snapshot *snapshot;
 | |
| 	char msg_to[128];
 | |
| 	char *tech = ast_strdupa(args->tech);
 | |
| 
 | |
| 	/* Really, we just want to know if this thing exists */
 | |
| 	snapshot = ast_endpoint_latest_snapshot(args->tech, args->resource);
 | |
| 	if (!snapshot) {
 | |
| 		ast_ari_response_error(response, 404, "Not Found",
 | |
| 			"Endpoint not found");
 | |
| 		return;
 | |
| 	}
 | |
| 	ao2_ref(snapshot, -1);
 | |
| 
 | |
| 	if (args->variables) {
 | |
| 		struct ast_json *json_variables;
 | |
| 
 | |
| 		ast_ari_endpoints_send_message_to_endpoint_parse_body(args->variables, args);
 | |
| 		json_variables = ast_json_object_get(args->variables, "variables");
 | |
| 		if (json_variables
 | |
| 			&& json_to_ast_variables(response, json_variables, &variables)) {
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	snprintf(msg_to, sizeof(msg_to), "%s:%s", ast_str_to_lower(tech), args->resource);
 | |
| 
 | |
| 	send_message(msg_to, args->from, args->body, variables, response);
 | |
| 	ast_variables_destroy(variables);
 | |
| }
 | |
| 
 | |
| static void send_refer(const char *to, const char *from, const char *refer_to, int to_self, struct ast_variable *variables, struct ast_ari_response *response)
 | |
| {
 | |
| 	struct ast_variable *current;
 | |
| 	struct ast_refer *refer;
 | |
| 	int res = 0;
 | |
| 
 | |
| 	if (ast_strlen_zero(to)) {
 | |
| 		ast_ari_response_error(response, 400, "Bad Request",
 | |
| 			"To must be specified");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	refer = ast_refer_alloc();
 | |
| 	if (!refer) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ast_refer_set_to(refer, "%s", to);
 | |
| 	ast_refer_set_to_self(refer, to_self);
 | |
| 
 | |
| 	if (!ast_strlen_zero(from)) {
 | |
| 		ast_refer_set_from(refer, "%s", from);
 | |
| 	}
 | |
| 	if (!ast_strlen_zero(refer_to)) {
 | |
| 		ast_refer_set_refer_to(refer, "%s", refer_to);
 | |
| 	}
 | |
| 
 | |
| 	for (current = variables; current; current = current->next) {
 | |
| 		res |= ast_refer_set_var_outbound(refer, current->name, current->value);
 | |
| 	}
 | |
| 
 | |
| 	if (res) {
 | |
| 		ast_ari_response_alloc_failed(response);
 | |
| 		ast_refer_destroy(refer);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (ast_refer_send(refer)) {
 | |
| 		ast_ari_response_error(response, 404, "Not Found",
 | |
| 			"Endpoint not found");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	response->message = ast_json_null();
 | |
| 	response->response_code = 202;
 | |
| 	response->response_text = "Accepted";
 | |
| }
 | |
| 
 | |
| static int parse_refer_json(struct ast_json *body,
 | |
| 	struct ast_ari_response *response,
 | |
| 	struct ast_variable **variables)
 | |
| {
 | |
| 	const char *known_variables[] = { "display_name" };
 | |
| 	const char *value;
 | |
| 	struct ast_variable *new_var;
 | |
| 	struct ast_json *json_variable;
 | |
| 	int err = 0;
 | |
| 	int i;
 | |
| 
 | |
| 	if (!body) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	json_variable = ast_json_object_get(body, "variables");
 | |
| 	if (json_variable) {
 | |
| 		err = json_to_ast_variables(response, json_variable, variables);
 | |
| 		if (err) {
 | |
| 			return err;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < sizeof(known_variables) / sizeof(*known_variables); ++i) {
 | |
| 		json_variable = ast_json_object_get(body, known_variables[i]);
 | |
| 		if (json_variable && ast_json_typeof(json_variable) == AST_JSON_STRING) {
 | |
| 			value = ast_json_string_get(json_variable);
 | |
| 			new_var = ast_variable_new(known_variables[i], value, "");
 | |
| 			if (new_var) {
 | |
| 				ast_variable_list_append(variables, new_var);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| void ast_ari_endpoints_refer(struct ast_variable *headers,
 | |
| 	struct ast_ari_endpoints_refer_args *args,
 | |
| 	struct ast_ari_response *response)
 | |
| {
 | |
| 	struct ast_variable *variables = NULL;
 | |
| 
 | |
| 	ast_ari_endpoints_refer_parse_body(args->variables, args);
 | |
| 
 | |
| 	if (parse_refer_json(args->variables, response, &variables)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	send_refer(args->to, args->from, args->refer_to, args->to_self, variables, response);
 | |
| 	ast_variables_destroy(variables);
 | |
| }
 | |
| 
 | |
| void ast_ari_endpoints_refer_to_endpoint(struct ast_variable *headers,
 | |
| 	struct ast_ari_endpoints_refer_to_endpoint_args *args,
 | |
| 	struct ast_ari_response *response)
 | |
| {
 | |
| 	struct ast_variable *variables = NULL;
 | |
| 	struct ast_endpoint_snapshot *snapshot;
 | |
| 	char to[128];
 | |
| 	char *tech = ast_strdupa(args->tech);
 | |
| 
 | |
| 	/* Really, we just want to know if this thing exists */
 | |
| 	snapshot = ast_endpoint_latest_snapshot(args->tech, args->resource);
 | |
| 	if (!snapshot) {
 | |
| 		ast_ari_response_error(response, 404, "Not Found",
 | |
| 			"Endpoint not found");
 | |
| 		return;
 | |
| 	}
 | |
| 	ao2_ref(snapshot, -1);
 | |
| 
 | |
| 	ast_ari_endpoints_refer_to_endpoint_parse_body(args->variables, args);
 | |
| 
 | |
| 	if (parse_refer_json(args->variables, response, &variables)) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	snprintf(to, sizeof(to), "%s:%s", ast_str_to_lower(tech), args->resource);
 | |
| 
 | |
| 	send_refer(to, args->from, args->refer_to, args->to_self, variables, response);
 | |
| 	ast_variables_destroy(variables);
 | |
| }
 |