Esempio n. 1
0
void ast_ari_asterisk_get_global_var(struct ast_variable *headers,
	struct ast_ari_asterisk_get_global_var_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
	RAII_VAR(struct ast_str *, tmp, NULL, ast_free);

	const char *value;

	ast_assert(response != NULL);

	if (ast_strlen_zero(args->variable)) {
		ast_ari_response_error(
			response, 400, "Bad Request",
			"Variable name is required");
		return;
	}

	tmp = ast_str_create(32);
	if (!tmp) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	value = ast_str_retrieve_variable(&tmp, 0, NULL, NULL, args->variable);

	if (!(json = ast_json_pack("{s: s}", "value", S_OR(value, "")))) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	ast_ari_response_ok(response, ast_json_ref(json));
}
Esempio n. 2
0
void ast_ari_channels_list(struct ast_variable *headers,
                           struct ast_ari_channels_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;
    struct stasis_message_sanitizer *sanitize = stasis_app_get_sanitizer();

    cache = ast_channel_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_channel_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;
    }

    for (i = ao2_iterator_init(snapshots, 0);
            (obj = ao2_iterator_next(&i)); ao2_cleanup(obj)) {
        RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
        struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
        int r;

        if (sanitize && sanitize->channel_snapshot
                && sanitize->channel_snapshot(snapshot)) {
            continue;
        }

        r = ast_json_array_append(
                json, ast_channel_snapshot_to_json(snapshot, NULL));
        if (r != 0) {
            ast_ari_response_alloc_failed(response);
            ao2_cleanup(obj);
            ao2_iterator_destroy(&i);
            return;
        }
    }
    ao2_iterator_destroy(&i);

    ast_ari_response_ok(response, ast_json_ref(json));
}
Esempio n. 3
0
/*!
 * \brief Performs common setup for a bridge playback operation
 * with both new controls and when existing controls are  found.
 *
 * \param args_media medias to play
 * \param args_media_count number of media items in \c media
 * \param args_lang language string split from arguments
 * \param args_offset_ms milliseconds offset split from arguments
 * \param args_playback_id string to use for playback split from
 *        arguments (null valid)
 * \param response ARI response being built
 * \param bridge Bridge the playback is being peformed on
 * \param control Control being used for the playback channel
 * \param json contents of the response to ARI
 * \param playback_url stores playback URL for use with response
 *
 * \retval -1 operation failed
 * \retval operation was successful
 */
static int ari_bridges_play_helper(const char **args_media,
	size_t args_media_count,
	const char *args_lang,
	int args_offset_ms,
	int args_skipms,
	const char *args_playback_id,
	struct ast_ari_response *response,
	struct ast_bridge *bridge,
	struct stasis_app_control *control,
	struct ast_json **json,
	char **playback_url)
{
	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
	RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);

	const char *language;

	snapshot = stasis_app_control_get_snapshot(control);
	if (!snapshot) {
		ast_ari_response_error(
			response, 500, "Internal Error", "Failed to get control snapshot");
		return -1;
	}

	language = S_OR(args_lang, snapshot->language);

	playback = stasis_app_control_play_uri(control, args_media, args_media_count,
		language, bridge->uniqueid, STASIS_PLAYBACK_TARGET_BRIDGE, args_skipms,
		args_offset_ms, args_playback_id);

	if (!playback) {
		ast_ari_response_alloc_failed(response);
		return -1;
	}

	if (ast_asprintf(playback_url, "/playbacks/%s",
			stasis_app_playback_get_id(playback)) == -1) {
		playback_url = NULL;
		ast_ari_response_alloc_failed(response);
		return -1;
	}

	*json = stasis_app_playback_to_json(playback);
	if (!*json) {
		ast_ari_response_alloc_failed(response);
		return -1;
	}

	return 0;
}
Esempio n. 4
0
void ast_ari_asterisk_get_module(struct ast_variable *headers,
	struct ast_ari_asterisk_get_module_args *args,
	struct ast_ari_response *response)
{
	struct ast_json *json;
	int module_retrieved = 0;

	ast_assert(response != NULL);

	if (!ast_module_check(args->module_name)) {
		ast_ari_response_error(
			response, 404, "Not Found",
			"Module could not be found in running modules");
		return;
	}

	json = ast_json_object_create();
	if (!json) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	module_retrieved = ast_update_module_list_condition(&identify_module, NULL, json,
	                                                    args->module_name);
	if (!module_retrieved) {
		ast_ari_response_error(
			response, 409, "Conflict",
			"Module information could not be retrieved");
		return;
	}

	ast_ari_response_ok(response, json);
}
Esempio n. 5
0
static void return_sorcery_object(struct ast_sorcery *sorcery, void *sorcery_obj,
	struct ast_ari_response *response)
{
	RAII_VAR(struct ast_json *, return_set, NULL, ast_json_unref);
	struct ast_variable *change_set;
	struct ast_variable *it_change_set;

	return_set = ast_json_array_create();
	if (!return_set) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	/* Note that we can't use the sorcery JSON change set directly,
	 * as it will hand us back an Object (with fields), and we need
	 * a more generic representation of whatever the API call asked
	 * for, i.e., a list of tuples.
	 */
	change_set = ast_sorcery_objectset_create(sorcery, sorcery_obj);
	if (!change_set) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) {
		struct ast_json *tuple;

		tuple = ast_json_pack("{s: s, s: s}",
			"attribute", it_change_set->name,
			"value", it_change_set->value);
		if (!tuple) {
			ast_variables_destroy(change_set);
			ast_ari_response_alloc_failed(response);
			return;
		}

		if (ast_json_array_append(return_set, tuple)) {
			ast_json_unref(tuple);
			ast_variables_destroy(change_set);
			ast_ari_response_alloc_failed(response);
			return;
		}
	}
	ast_variables_destroy(change_set);

	ast_ari_response_ok(response, ast_json_ref(return_set));
}
Esempio n. 6
0
void ast_ari_bridges_list(struct ast_variable *headers,
	struct ast_ari_bridges_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_bridge_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_bridge_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_bridge_snapshot *snapshot = stasis_message_data(msg);
		struct ast_json *json_bridge = ast_bridge_snapshot_to_json(snapshot, stasis_app_get_sanitizer());

		if (!json_bridge || ast_json_array_append(json, json_bridge)) {
			ao2_iterator_destroy(&i);
			ast_ari_response_alloc_failed(response);
			return;
		}
	}
	ao2_iterator_destroy(&i);

	ast_ari_response_ok(response, ast_json_ref(json));
}
Esempio n. 7
0
/*!
 * \brief Parameter parsing callback for /sounds.
 * \param get_params GET parameters in the HTTP request.
 * \param path_vars Path variables extracted from the request.
 * \param headers HTTP headers.
 * \param[out] response Response to the HTTP request.
 */
static void ast_ari_sounds_list_cb(
	struct ast_tcptls_session_instance *ser,
	struct ast_variable *get_params, struct ast_variable *path_vars,
	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
{
	struct ast_ari_sounds_list_args args = {};
	struct ast_variable *i;
#if defined(AST_DEVMODE)
	int is_valid;
	int code;
#endif /* AST_DEVMODE */

	for (i = get_params; i; i = i->next) {
		if (strcmp(i->name, "lang") == 0) {
			args.lang = (i->value);
		} else
		if (strcmp(i->name, "format") == 0) {
			args.format = (i->value);
		} else
		{}
	}
	if (ast_ari_sounds_list_parse_body(body, &args)) {
		ast_ari_response_alloc_failed(response);
		goto fin;
	}
	ast_ari_sounds_list(headers, &args, response);
#if defined(AST_DEVMODE)
	code = response->response_code;

	switch (code) {
	case 0: /* Implementation is still a stub, or the code wasn't set */
		is_valid = response->message == NULL;
		break;
	case 500: /* Internal Server Error */
	case 501: /* Not Implemented */
		is_valid = 1;
		break;
	default:
		if (200 <= code && code <= 299) {
			is_valid = ast_ari_validate_list(response->message,
				ast_ari_validate_sound_fn());
		} else {
			ast_log(LOG_ERROR, "Invalid error response %d for /sounds\n", code);
			is_valid = 0;
		}
	}

	if (!is_valid) {
		ast_log(LOG_ERROR, "Response validation failed for /sounds\n");
		ast_ari_response_error(response, 500,
			"Internal Server Error", "Response validation failed");
	}
#endif /* AST_DEVMODE */

fin: __attribute__((unused))
	return;
}
Esempio n. 8
0
void ast_ari_recordings_list_stored(struct ast_variable *headers,
	struct ast_ari_recordings_list_stored_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct ao2_container *, recordings, NULL, ao2_cleanup);
	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
	struct ao2_iterator i;
	void *obj;

	recordings = stasis_app_stored_recording_find_all();

	if (!recordings) {
		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(recordings, 0);
	while ((obj = ao2_iterator_next(&i))) {
		RAII_VAR(struct stasis_app_stored_recording *, recording, obj,
			ao2_cleanup);

		int r = ast_json_array_append(
			json, stasis_app_stored_recording_to_json(recording));
		if (r != 0) {
			ast_ari_response_alloc_failed(response);
			ao2_iterator_destroy(&i);
			return;
		}
	}
	ao2_iterator_destroy(&i);

	ast_ari_response_ok(response, ast_json_ref(json));
}
Esempio n. 9
0
void ast_ari_bridges_add_channel(struct ast_variable *headers,
	struct ast_ari_bridges_add_channel_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
	RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
	size_t i;
	int has_error = 0;

	if (!bridge) {
		/* Response filled in by find_bridge() */
		return;
	}

	list = control_list_create(response, args->channel_count, args->channel);
	if (!list) {
		/* Response filled in by control_list_create() */
		return;
	}

	for (i = 0; i < list->count; ++i) {
		stasis_app_control_clear_roles(list->controls[i]);
		if (!ast_strlen_zero(args->role)) {
			if (stasis_app_control_add_role(list->controls[i], args->role)) {
				ast_ari_response_alloc_failed(response);
				return;
			}
		}
	}

	for (i = 0; i < list->count; ++i) {
		if ((has_error = check_add_remove_channel(response, list->controls[i],
			     stasis_app_control_add_channel_to_bridge(
				    list->controls[i], bridge)))) {
			break;
		}
	}

	if (!has_error) {
		ast_ari_response_no_content(response);
	}
}
void ast_ari_channels_continue_in_dialplan(
	struct ast_variable *headers,
	struct ast_ari_channels_continue_in_dialplan_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);

	ast_assert(response != NULL);

	control = find_control(response, args->channel_id);
	if (control == NULL) {
		return;
	}

	if (stasis_app_control_continue(control, args->context, args->extension, args->priority)) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	ast_ari_response_no_content(response);
}
Esempio n. 11
0
void ast_ari_endpoints_get(struct ast_variable *headers,
	struct ast_ari_endpoints_get_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
	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, ast_json_ref(json));
}
Esempio n. 12
0
void ast_ari_bridges_start_moh(struct ast_variable *headers,
	struct ast_ari_bridges_start_moh_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
	struct ast_channel *moh_channel;
	const char *moh_class = args->moh_class;

	if (!bridge) {
		/* The response is provided by find_bridge() */
		return;
	}

	moh_channel = stasis_app_bridge_moh_channel(bridge);
	if (!moh_channel) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	ast_moh_start(moh_channel, moh_class, NULL);

	ast_ari_response_no_content(response);

}
Esempio n. 13
0
static struct control_list *control_list_create(struct ast_ari_response *response, size_t count, const char **channels) {
	RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
	size_t i;

	if (count == 0 || !channels) {
		ast_ari_response_error(response, 400, "Bad Request", "Missing parameter channel");
		return NULL;
	}

	list = ao2_alloc(sizeof(*list) + count * sizeof(list->controls[0]), control_list_dtor);
	if (!list) {
		ast_ari_response_alloc_failed(response);
		return NULL;
	}

	for (i = 0; i < count; ++i) {
		if (ast_strlen_zero(channels[i])) {
			continue;
		}
		list->controls[list->count] =
			find_channel_control(response, channels[i]);
		if (!list->controls[list->count]) {
			/* response filled in by find_channel_control() */
			return NULL;
		}
		++list->count;
	}

	if (list->count == 0) {
		ast_ari_response_error(response, 400, "Bad Request", "Missing parameter channel");
		return NULL;
	}

	ao2_ref(list, +1);
	return list;
}
Esempio n. 14
0
void ast_ari_asterisk_get_info(struct ast_variable *headers,
	struct ast_ari_asterisk_get_info_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
	int show_all = args->only_count == 0;
	int show_build = show_all;
	int show_system = show_all;
	int show_config = show_all;
	int show_status = show_all;
	size_t i;
	int res = 0;

	for (i = 0; i < args->only_count; ++i) {
		if (strcasecmp("build", args->only[i]) == 0) {
			show_build = 1;
		} else if (strcasecmp("system", args->only[i]) == 0) {
			show_system = 1;
		} else if (strcasecmp("config", args->only[i]) == 0) {
			show_config = 1;
		} else if (strcasecmp("status", args->only[i]) == 0) {
			show_status = 1;
		} else {
			ast_log(LOG_WARNING, "Unrecognized info section '%s'\n",
				args->only[i]);
		}
	}

	json = ast_json_object_create();

	if (show_build) {
		res |= ast_json_object_set(json, "build",
			ast_json_pack(
				"{ s: s, s: s, s: s,"
				"  s: s, s: s, s: s }",

				"os", ast_build_os,
				"kernel", ast_build_kernel,
				"machine", ast_build_machine,

				"options", AST_BUILDOPTS,
				"date", ast_build_date,
				"user", ast_build_user));
	}

	if (show_system) {
		char eid_str[128];

		ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);

		res |= ast_json_object_set(json, "system",
			ast_json_pack("{ s: s, s: s }",
				"version", ast_get_version(),
				"entity_id", eid_str));
	}

	if (show_config) {
		struct ast_json *config = ast_json_pack(
			"{ s: s, s: s,"
			" s: { s: s, s: s } }",

			"name", ast_config_AST_SYSTEM_NAME,
			"default_language", ast_defaultlanguage,

			"setid",
			"user", ast_config_AST_RUN_USER,
			"group", ast_config_AST_RUN_GROUP);

		res |= ast_json_object_set(json, "config", config);

		if (ast_option_maxcalls) {
			res |= ast_json_object_set(config, "max_channels",
				ast_json_integer_create(ast_option_maxcalls));
		}

		if (ast_option_maxfiles) {
			res |= ast_json_object_set(config, "max_open_files",
				ast_json_integer_create(ast_option_maxfiles));
		}

		if (ast_option_maxload) {
			res |= ast_json_object_set(config, "max_load",
				ast_json_real_create(ast_option_maxload));
		}
	}

	if (show_status) {
		res |= ast_json_object_set(json, "status",
			ast_json_pack("{ s: o, s: o }",
				"startup_time",
				ast_json_timeval(ast_startuptime, NULL),
				"last_reload_time",
				ast_json_timeval(ast_lastreloadtime, NULL)));
	}

	if (res != 0) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	ast_ari_response_ok(response, ast_json_ref(json));
}
Esempio n. 15
0
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));
}
Esempio n. 16
0
static void ari_bridges_play_new(const char *args_media,
	const char *args_lang,
	int args_offset_ms,
	int args_skipms,
	const char *args_playback_id,
	struct ast_ari_response *response,
	struct ast_bridge *bridge)
{
	RAII_VAR(struct ast_channel *, play_channel, NULL, ast_hangup);
	RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
	RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
	RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel);
	RAII_VAR(char *, playback_url, NULL, ast_free);

	struct stasis_topic *channel_topic;
	struct stasis_topic *bridge_topic;
	struct bridge_channel_control_thread_data *thread_data;
	pthread_t threadid;

	if (!(play_channel = prepare_bridge_media_channel("Announcer"))) {
		ast_ari_response_error(
			response, 500, "Internal Error", "Could not create playback channel");
		return;
	}
	ast_debug(1, "Created announcer channel '%s'\n", ast_channel_name(play_channel));

	bridge_topic = ast_bridge_topic(bridge);
	channel_topic = ast_channel_topic(play_channel);

	/* Forward messages from the playback channel topic to the bridge topic so that anything listening for
	 * messages on the bridge topic will receive the playback 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 playback channel stasis messages to bridge topic");
		return;
	}

	if (ast_unreal_channel_push_to_bridge(play_channel, bridge,
		AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE | AST_BRIDGE_CHANNEL_FLAG_LONELY)) {
		ast_ari_response_error(
			response, 500, "Internal Error", "Failed to put playback channel into the bridge");
		return;
	}

	control = stasis_app_control_create(play_channel);
	if (control == NULL) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	ao2_lock(control);
	if (ari_bridges_play_helper(args_media, args_lang, args_offset_ms,
			args_skipms, args_playback_id, response, bridge, control,
			&json, &playback_url)) {
		ao2_unlock(control);
		return;
	}
	ao2_unlock(control);

	if (stasis_app_bridge_playback_channel_add(bridge, play_channel, control)) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	/* Give play_channel and control reference to the thread data */
	thread_data = ast_calloc(1, sizeof(*thread_data));
	if (!thread_data) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	thread_data->bridge_channel = play_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. */
	play_channel = NULL;
	control = NULL;
	channel_forward = NULL;

	ast_ari_response_created(response, playback_url, ast_json_ref(json));
}
Esempio n. 17
0
/*!
 * \brief Parameter parsing callback for /asterisk/variable.
 * \param get_params GET parameters in the HTTP request.
 * \param path_vars Path variables extracted from the request.
 * \param headers HTTP headers.
 * \param[out] response Response to the HTTP request.
 */
static void ast_ari_asterisk_set_global_var_cb(
	struct ast_tcptls_session_instance *ser,
	struct ast_variable *get_params, struct ast_variable *path_vars,
	struct ast_variable *headers, struct ast_ari_response *response)
{
	struct ast_ari_asterisk_set_global_var_args args = {};
	struct ast_variable *i;
	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
#if defined(AST_DEVMODE)
	int is_valid;
	int code;
#endif /* AST_DEVMODE */

	for (i = get_params; i; i = i->next) {
		if (strcmp(i->name, "variable") == 0) {
			args.variable = (i->value);
		} else
		if (strcmp(i->name, "value") == 0) {
			args.value = (i->value);
		} else
		{}
	}
	/* Look for a JSON request entity */
	body = ast_http_get_json(ser, headers);
	if (!body) {
		switch (errno) {
		case EFBIG:
			ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
			goto fin;
		case ENOMEM:
			ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
			goto fin;
		case EIO:
			ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
			goto fin;
		}
	}
	if (ast_ari_asterisk_set_global_var_parse_body(body, &args)) {
		ast_ari_response_alloc_failed(response);
		goto fin;
	}
	ast_ari_asterisk_set_global_var(headers, &args, response);
#if defined(AST_DEVMODE)
	code = response->response_code;

	switch (code) {
	case 0: /* Implementation is still a stub, or the code wasn't set */
		is_valid = response->message == NULL;
		break;
	case 500: /* Internal Server Error */
	case 501: /* Not Implemented */
	case 400: /* Missing variable parameter. */
		is_valid = 1;
		break;
	default:
		if (200 <= code && code <= 299) {
			is_valid = ast_ari_validate_void(
				response->message);
		} else {
			ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/variable\n", code);
			is_valid = 0;
		}
	}

	if (!is_valid) {
		ast_log(LOG_ERROR, "Response validation failed for /asterisk/variable\n");
		ast_ari_response_error(response, 500,
			"Internal Server Error", "Response validation failed");
	}
#endif /* AST_DEVMODE */

fin: __attribute__((unused))
	return;
}
Esempio n. 18
0
void ast_ari_get_docs(const char *uri, struct ast_variable *headers,
                      struct ast_ari_response *response)
{
    RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
    RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
    RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
    struct ast_json *obj = NULL;
    struct ast_variable *host = NULL;
    struct ast_json_error error = {};
    struct stat file_stat;

    ast_debug(3, "%s(%s)\n", __func__, uri);

    absolute_path_builder = ast_str_create(80);
    if (absolute_path_builder == NULL) {
        ast_ari_response_alloc_failed(response);
        return;
    }

    /* absolute path to the rest-api directory */
    ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
    ast_str_append(&absolute_path_builder, 0, "/rest-api/");
    absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
    if (absolute_api_dirname == NULL) {
        ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
        ast_ari_response_error(
            response, 500, "Internal Server Error",
            "Cannot find rest-api directory");
        return;
    }

    /* absolute path to the requested file */
    ast_str_append(&absolute_path_builder, 0, "%s", uri);
    absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
    if (absolute_filename == NULL) {
        switch (errno) {
        case ENAMETOOLONG:
        case ENOENT:
        case ENOTDIR:
            ast_ari_response_error(
                response, 404, "Not Found",
                "Resource not found");
            break;
        case EACCES:
            ast_ari_response_error(
                response, 403, "Forbidden",
                "Permission denied");
            break;
        default:
            ast_log(LOG_ERROR,
                    "Error determining real path for uri '%s': %s\n",
                    uri, strerror(errno));
            ast_ari_response_error(
                response, 500, "Internal Server Error",
                "Cannot find file");
            break;
        }
        return;
    }

    if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
        /* HACKERZ! */
        ast_log(LOG_ERROR,
                "Invalid attempt to access '%s' (not in %s)\n",
                absolute_filename, absolute_api_dirname);
        ast_ari_response_error(
            response, 404, "Not Found",
            "Resource not found");
        return;
    }

    if (stat(absolute_filename, &file_stat) == 0) {
        if (!(file_stat.st_mode & S_IFREG)) {
            /* Not a file */
            ast_ari_response_error(
                response, 403, "Forbidden",
                "Invalid access");
            return;
        }
    } else {
        /* Does not exist */
        ast_ari_response_error(
            response, 404, "Not Found",
            "Resource not found");
        return;
    }

    /* Load resource object from file */
    obj = ast_json_load_new_file(absolute_filename, &error);
    if (obj == NULL) {
        ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
                error.source, error.line, error.column, error.text);
        ast_ari_response_error(
            response, 500, "Internal Server Error",
            "Yikes! Cannot parse resource");
        return;
    }

    /* Update the basePath properly */
    if (ast_json_object_get(obj, "basePath") != NULL) {
        for (host = headers; host; host = host->next) {
            if (strcasecmp(host->name, "Host") == 0) {
                break;
            }
        }
        if (host != NULL) {
            ast_json_object_set(
                obj, "basePath",
                ast_json_stringf("http://%s/ari", host->value));
        } else {
            /* Without the host, we don't have the basePath */
            ast_json_object_del(obj, "basePath");
        }
    }

    ast_ari_response_ok(response, obj);
}
Esempio n. 19
0
void ast_ari_channels_continue_in_dialplan(
	struct ast_variable *headers,
	struct ast_ari_channels_continue_in_dialplan_args *args,
	struct ast_ari_response *response)
{
	RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
	int ipri;
	const char *context;
	const char *exten;

	ast_assert(response != NULL);

	control = find_control(response, args->channel_id);
	if (control == NULL) {
		return;
	}

	snapshot = stasis_app_control_get_snapshot(control);
	if (!snapshot) {
		return;
	}

	if (ast_strlen_zero(args->context)) {
		context = snapshot->context;
		exten = S_OR(args->extension, snapshot->exten);
	} else {
		context = args->context;
		exten = S_OR(args->extension, "s");
	}

	if (!ast_strlen_zero(args->label)) {
		/* A label was provided in the request, use that */

		if (sscanf(args->label, "%30d", &ipri) != 1) {
			ipri = ast_findlabel_extension(NULL, context, exten, args->label, NULL);
			if (ipri == -1) {
				ast_log(AST_LOG_ERROR, "Requested label: %s can not be found in context: %s\n", args->label, context);
				ast_ari_response_error(response, 404, "Not Found", "Requested label can not be found");
				return;
			}
		} else {
			ast_debug(3, "Numeric value provided for label, jumping to that priority\n");
		}

		if (ipri == 0) {
			ast_log(AST_LOG_ERROR, "Invalid priority label '%s' specified for extension %s in context: %s\n",
					args->label, exten, context);
			ast_ari_response_error(response, 400, "Bad Request", "Requested priority is illegal");
			return;
		}

	} else if (args->priority) {
		/* No label provided, use provided priority */
		ipri = args->priority;
	} else if (ast_strlen_zero(args->context) && ast_strlen_zero(args->extension)) {
		/* Special case. No exten, context, or priority provided, then move on to the next priority */
		ipri = snapshot->priority + 1;
	} else {
		ipri = 1;
	}


	if (stasis_app_control_continue(control, context, exten, ipri)) {
		ast_ari_response_alloc_failed(response);
		return;
	}

	ast_ari_response_no_content(response);
}
Esempio n. 20
0
/*!
 * \brief Parameter parsing callback for /recordings/stored/{recordingName}/copy.
 * \param get_params GET parameters in the HTTP request.
 * \param path_vars Path variables extracted from the request.
 * \param headers HTTP headers.
 * \param[out] response Response to the HTTP request.
 */
static void ast_ari_recordings_copy_stored_cb(
	struct ast_tcptls_session_instance *ser,
	struct ast_variable *get_params, struct ast_variable *path_vars,
	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
{
	struct ast_ari_recordings_copy_stored_args args = {};
	struct ast_variable *i;
#if defined(AST_DEVMODE)
	int is_valid;
	int code;
#endif /* AST_DEVMODE */

	for (i = get_params; i; i = i->next) {
		if (strcmp(i->name, "destinationRecordingName") == 0) {
			args.destination_recording_name = (i->value);
		} else
		{}
	}
	for (i = path_vars; i; i = i->next) {
		if (strcmp(i->name, "recordingName") == 0) {
			args.recording_name = (i->value);
		} else
		{}
	}
	if (ast_ari_recordings_copy_stored_parse_body(body, &args)) {
		ast_ari_response_alloc_failed(response);
		goto fin;
	}
	ast_ari_recordings_copy_stored(headers, &args, response);
#if defined(AST_DEVMODE)
	code = response->response_code;

	switch (code) {
	case 0: /* Implementation is still a stub, or the code wasn't set */
		is_valid = response->message == NULL;
		break;
	case 500: /* Internal Server Error */
	case 501: /* Not Implemented */
	case 404: /* Recording not found */
	case 409: /* A recording with the same name already exists on the system */
		is_valid = 1;
		break;
	default:
		if (200 <= code && code <= 299) {
			is_valid = ast_ari_validate_stored_recording(
				response->message);
		} else {
			ast_log(LOG_ERROR, "Invalid error response %d for /recordings/stored/{recordingName}/copy\n", code);
			is_valid = 0;
		}
	}

	if (!is_valid) {
		ast_log(LOG_ERROR, "Response validation failed for /recordings/stored/{recordingName}/copy\n");
		ast_ari_response_error(response, 500,
			"Internal Server Error", "Response validation failed");
	}
#endif /* AST_DEVMODE */

fin: __attribute__((unused))
	return;
}
Esempio n. 21
0
/*!
 * \brief Parameter parsing callback for /bridges/{bridgeId}/removeChannel.
 * \param get_params GET parameters in the HTTP request.
 * \param path_vars Path variables extracted from the request.
 * \param headers HTTP headers.
 * \param[out] response Response to the HTTP request.
 */
static void ast_ari_bridges_remove_channel_cb(
	struct ast_tcptls_session_instance *ser,
	struct ast_variable *get_params, struct ast_variable *path_vars,
	struct ast_variable *headers, struct ast_ari_response *response)
{
	struct ast_ari_bridges_remove_channel_args args = {};
	struct ast_variable *i;
	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
#if defined(AST_DEVMODE)
	int is_valid;
	int code;
#endif /* AST_DEVMODE */

	for (i = get_params; i; i = i->next) {
		if (strcmp(i->name, "channel") == 0) {
			/* Parse comma separated list */
			char *vals[MAX_VALS];
			size_t j;

			args.channel_parse = ast_strdup(i->value);
			if (!args.channel_parse) {
				ast_ari_response_alloc_failed(response);
				goto fin;
			}

			if (strlen(args.channel_parse) == 0) {
				/* ast_app_separate_args can't handle "" */
				args.channel_count = 1;
				vals[0] = args.channel_parse;
			} else {
				args.channel_count = ast_app_separate_args(
					args.channel_parse, ',', vals,
					ARRAY_LEN(vals));
			}

			if (args.channel_count == 0) {
				ast_ari_response_alloc_failed(response);
				goto fin;
			}

			if (args.channel_count >= MAX_VALS) {
				ast_ari_response_error(response, 400,
					"Bad Request",
					"Too many values for channel");
				goto fin;
			}

			args.channel = ast_malloc(sizeof(*args.channel) * args.channel_count);
			if (!args.channel) {
				ast_ari_response_alloc_failed(response);
				goto fin;
			}

			for (j = 0; j < args.channel_count; ++j) {
				args.channel[j] = (vals[j]);
			}
		} else
		{}
	}
	for (i = path_vars; i; i = i->next) {
		if (strcmp(i->name, "bridgeId") == 0) {
			args.bridge_id = (i->value);
		} else
		{}
	}
	/* Look for a JSON request entity */
	body = ast_http_get_json(ser, headers);
	if (!body) {
		switch (errno) {
		case EFBIG:
			ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
			goto fin;
		case ENOMEM:
			ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
			goto fin;
		case EIO:
			ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
			goto fin;
		}
	}
	if (ast_ari_bridges_remove_channel_parse_body(body, &args)) {
		ast_ari_response_alloc_failed(response);
		goto fin;
	}
	ast_ari_bridges_remove_channel(headers, &args, response);
#if defined(AST_DEVMODE)
	code = response->response_code;

	switch (code) {
	case 0: /* Implementation is still a stub, or the code wasn't set */
		is_valid = response->message == NULL;
		break;
	case 500: /* Internal Server Error */
	case 501: /* Not Implemented */
	case 400: /* Channel not found */
	case 404: /* Bridge not found */
	case 409: /* Bridge not in Stasis application */
	case 422: /* Channel not in this bridge */
		is_valid = 1;
		break;
	default:
		if (200 <= code && code <= 299) {
			is_valid = ast_ari_validate_void(
				response->message);
		} else {
			ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/removeChannel\n", code);
			is_valid = 0;
		}
	}

	if (!is_valid) {
		ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/removeChannel\n");
		ast_ari_response_error(response, 500,
			"Internal Server Error", "Response validation failed");
	}
#endif /* AST_DEVMODE */

fin: __attribute__((unused))
	ast_free(args.channel_parse);
	ast_free(args.channel);
	return;
}
Esempio n. 22
0
void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response)
{
	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
	RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
	RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
	struct ast_json *fields;
	struct ast_variable *update_set = NULL;
	int created = 0;

	sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
	if (!sorcery) {
		ast_ari_response_error(
			response, 404, "Not Found",
			"configClass '%s' not found",
			args->config_class);
		return;
	}

	object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
	if (!object_type) {
		ast_ari_response_error(
			response, 404, "Not Found",
			"objectType '%s' not found",
			args->object_type);
		return;
	}

	sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
	if (!sorcery_obj) {
		ast_debug(5, "Sorcery object '%s' does not exist; creating it\n", args->id);
		sorcery_obj = ast_sorcery_alloc(sorcery, args->object_type, args->id);
		if (!sorcery_obj) {
			ast_ari_response_alloc_failed(response);
			return;
		}

		created = 1;
	} else {
		void *copy;

		copy = ast_sorcery_copy(sorcery, sorcery_obj);
		if (!copy) {
			ast_ari_response_alloc_failed(response);
			return;
		}

		ao2_ref(sorcery_obj, -1);
		sorcery_obj = copy;
	}

	fields = ast_json_object_get(args->fields, "fields");
	if (!fields && !created) {
		/* Whoops. We need data. */
		ast_ari_response_error(
			response, 400, "Bad request",
			"Fields must be provided to update object '%s'",
			args->id);
		return;
	} else if (fields) {
		size_t i;

		for (i = 0; i < ast_json_array_size(fields); i++) {
			struct ast_variable *new_var;
			struct ast_json *json_value = ast_json_array_get(fields, i);

			if (!json_value) {
				continue;
			}

			new_var = ast_variable_new(
				ast_json_string_get(ast_json_object_get(json_value, "attribute")),
				ast_json_string_get(ast_json_object_get(json_value, "value")),
				"");
			if (!new_var) {
				ast_variables_destroy(update_set);
				ast_ari_response_alloc_failed(response);
				return;
			}
			ast_variable_list_append(&update_set, new_var);
		}
	}

	/* APPLY! Note that a NULL update_set is fine (and necessary), as it
	 * will force validation on a newly created object.
	 */
	if (ast_sorcery_objectset_apply(sorcery, sorcery_obj, update_set)) {
		ast_variables_destroy(update_set);
		ast_ari_response_error(
			response, 400, "Bad request",
			"%s of object '%s' failed field value validation",
			created ? "Creation" : "Update",
			args->id);
		return;
	}

	ast_variables_destroy(update_set);

	if (created) {
		if (ast_sorcery_create(sorcery, sorcery_obj)) {
			ast_ari_response_error(
				response, 403, "Forbidden",
				"Cannot create sorcery objects of type '%s'",
				args->object_type);
			return;
		}
	} else {
		if (ast_sorcery_update(sorcery, sorcery_obj)) {
			ast_ari_response_error(
				response, 403, "Forbidden",
				"Cannot update sorcery objects of type '%s'",
				args->object_type);
			return;
		}
	}

	return_sorcery_object(sorcery, sorcery_obj, response);
}
Esempio n. 23
0
static void ari_channels_handle_originate_with_id(const char *args_endpoint,
        const char *args_extension,
        const char *args_context,
        long args_priority,
        const char *args_app,
        const char *args_app_args,
        const char *args_caller_id,
        int args_timeout,
        struct ast_variable *variables,
        const char *args_channel_id,
        const char *args_other_channel_id,
        struct ast_ari_response *response)
{
    char *dialtech;
    char dialdevice[AST_CHANNEL_NAME];
    char *caller_id = NULL;
    char *cid_num = NULL;
    char *cid_name = NULL;
    int timeout = 30000;
    RAII_VAR(struct ast_format_cap *, cap,
             ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_NOLOCK), ast_format_cap_destroy);
    struct ast_format tmp_fmt;
    char *stuff;
    struct ast_channel *chan;
    RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
    struct ast_assigned_ids assignedids = {
        .uniqueid = args_channel_id,
        .uniqueid2 = args_other_channel_id,
    };

    if (!cap) {
        ast_ari_response_alloc_failed(response);
        return;
    }
    ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));

    if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid))
            || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) {
        ast_ari_response_error(response, 400, "Bad Request",
                               "Uniqueid length exceeds maximum of %d\n", AST_MAX_PUBLIC_UNIQUEID);
        return;
    }

    if (ast_strlen_zero(args_endpoint)) {
        ast_ari_response_error(response, 400, "Bad Request",
                               "Endpoint must be specified");
        return;
    }

    dialtech = ast_strdupa(args_endpoint);
    if ((stuff = strchr(dialtech, '/'))) {
        *stuff++ = '\0';
        ast_copy_string(dialdevice, stuff, sizeof(dialdevice));
    }

    if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) {
        ast_ari_response_error(response, 400, "Bad Request",
                               "Invalid endpoint specified");
        return;
    }

    if (args_timeout > 0) {
        timeout = args_timeout * 1000;
    } else if (args_timeout == -1) {
        timeout = -1;
    }

    if (!ast_strlen_zero(args_caller_id)) {
        caller_id = ast_strdupa(args_caller_id);
        ast_callerid_parse(caller_id, &cid_name, &cid_num);

        if (ast_is_shrinkable_phonenumber(cid_num)) {
            ast_shrink_phone_number(cid_num);
        }
    }

    if (!ast_strlen_zero(args_app)) {
        const char *app = "Stasis";

        RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free);

        if (!appdata) {
            ast_ari_response_alloc_failed(response);
            return;
        }

        ast_str_set(&appdata, 0, "%s", args_app);
        if (!ast_strlen_zero(args_app_args)) {
            ast_str_append(&appdata, 0, ",%s", args_app_args);
        }

        /* originate a channel, putting it into an application */
        if (ast_pbx_outgoing_app(dialtech, cap, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, variables, NULL, &chan, &assignedids)) {
            ast_ari_response_alloc_failed(response);
            return;
        }
    } else if (!ast_strlen_zero(args_extension)) {
Esempio n. 24
0
/*!
 * \brief Parameter parsing callback for /applications/{applicationName}/subscription.
 * \param get_params GET parameters in the HTTP request.
 * \param path_vars Path variables extracted from the request.
 * \param headers HTTP headers.
 * \param[out] response Response to the HTTP request.
 */
static void ast_ari_applications_unsubscribe_cb(
	struct ast_tcptls_session_instance *ser,
	struct ast_variable *get_params, struct ast_variable *path_vars,
	struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
{
	struct ast_ari_applications_unsubscribe_args args = {};
	struct ast_variable *i;
#if defined(AST_DEVMODE)
	int is_valid;
	int code;
#endif /* AST_DEVMODE */

	for (i = get_params; i; i = i->next) {
		if (strcmp(i->name, "eventSource") == 0) {
			/* Parse comma separated list */
			char *vals[MAX_VALS];
			size_t j;

			args.event_source_parse = ast_strdup(i->value);
			if (!args.event_source_parse) {
				ast_ari_response_alloc_failed(response);
				goto fin;
			}

			if (strlen(args.event_source_parse) == 0) {
				/* ast_app_separate_args can't handle "" */
				args.event_source_count = 1;
				vals[0] = args.event_source_parse;
			} else {
				args.event_source_count = ast_app_separate_args(
					args.event_source_parse, ',', vals,
					ARRAY_LEN(vals));
			}

			if (args.event_source_count == 0) {
				ast_ari_response_alloc_failed(response);
				goto fin;
			}

			if (args.event_source_count >= MAX_VALS) {
				ast_ari_response_error(response, 400,
					"Bad Request",
					"Too many values for event_source");
				goto fin;
			}

			args.event_source = ast_malloc(sizeof(*args.event_source) * args.event_source_count);
			if (!args.event_source) {
				ast_ari_response_alloc_failed(response);
				goto fin;
			}

			for (j = 0; j < args.event_source_count; ++j) {
				args.event_source[j] = (vals[j]);
			}
		} else
		{}
	}
	for (i = path_vars; i; i = i->next) {
		if (strcmp(i->name, "applicationName") == 0) {
			args.application_name = (i->value);
		} else
		{}
	}
	if (ast_ari_applications_unsubscribe_parse_body(body, &args)) {
		ast_ari_response_alloc_failed(response);
		goto fin;
	}
	ast_ari_applications_unsubscribe(headers, &args, response);
#if defined(AST_DEVMODE)
	code = response->response_code;

	switch (code) {
	case 0: /* Implementation is still a stub, or the code wasn't set */
		is_valid = response->message == NULL;
		break;
	case 500: /* Internal Server Error */
	case 501: /* Not Implemented */
	case 400: /* Missing parameter; event source scheme not recognized. */
	case 404: /* Application does not exist. */
	case 409: /* Application not subscribed to event source. */
	case 422: /* Event source does not exist. */
		is_valid = 1;
		break;
	default:
		if (200 <= code && code <= 299) {
			is_valid = ast_ari_validate_application(
				response->message);
		} else {
			ast_log(LOG_ERROR, "Invalid error response %d for /applications/{applicationName}/subscription\n", code);
			is_valid = 0;
		}
	}

	if (!is_valid) {
		ast_log(LOG_ERROR, "Response validation failed for /applications/{applicationName}/subscription\n");
		ast_ari_response_error(response, 500,
			"Internal Server Error", "Response validation failed");
	}
#endif /* AST_DEVMODE */

fin: __attribute__((unused))
	ast_free(args.event_source_parse);
	ast_free(args.event_source);
	return;
}
static void ari_channels_handle_originate_with_id(const char *args_endpoint,
	const char *args_extension,
	const char *args_context,
	long args_priority,
	const char *args_app,
	const char *args_app_args,
	const char *args_caller_id,
	int args_timeout,
	struct ast_variable *variables,
	const char *args_channel_id,
	const char *args_other_channel_id,
	const char *args_originator,
	struct ast_ari_response *response)
{
	char *dialtech;
	char dialdevice[AST_CHANNEL_NAME];
	struct ast_dial *dial;
	char *caller_id = NULL;
	char *cid_num = NULL;
	char *cid_name = NULL;
	RAII_VAR(struct ast_format_cap *, cap,
		ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
	char *stuff;
	struct ast_channel *other = NULL;
	struct ast_channel *chan;
	RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
	struct ast_assigned_ids assignedids = {
		.uniqueid = args_channel_id,
		.uniqueid2 = args_other_channel_id,
	};
	struct ari_origination *origination;
	pthread_t thread;

	if (!cap) {
		ast_ari_response_alloc_failed(response);
		return;
	}
	ast_format_cap_append(cap, ast_format_slin, 0);

	if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid))
		|| (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) {
		ast_ari_response_error(response, 400, "Bad Request",
			"Uniqueid length exceeds maximum of %d", AST_MAX_PUBLIC_UNIQUEID);
		return;
	}

	if (ast_strlen_zero(args_endpoint)) {
		ast_ari_response_error(response, 400, "Bad Request",
			"Endpoint must be specified");
		return;
	}

	dialtech = ast_strdupa(args_endpoint);
	if ((stuff = strchr(dialtech, '/'))) {
		*stuff++ = '\0';
		ast_copy_string(dialdevice, stuff, sizeof(dialdevice));
	}

	if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) {
		ast_ari_response_error(response, 400, "Bad Request",
			"Invalid endpoint specified");
		return;
	}

	if (!ast_strlen_zero(args_app)) {
		RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free);

		if (!appdata) {
			ast_ari_response_alloc_failed(response);
			return;
		}

		ast_str_set(&appdata, 0, "%s", args_app);
		if (!ast_strlen_zero(args_app_args)) {
			ast_str_append(&appdata, 0, ",%s", args_app_args);
		}

		origination = ast_calloc(1, sizeof(*origination) + ast_str_size(appdata) + 1);
		if (!origination) {
			ast_ari_response_alloc_failed(response);
			return;
		}

		strcpy(origination->appdata, ast_str_buffer(appdata));
	} else if (!ast_strlen_zero(args_extension)) {
Esempio n. 26
0
void ast_ari_channels_originate(struct ast_variable *headers,
                                struct ast_ari_channels_originate_args *args,
                                struct ast_ari_response *response)
{
    char *dialtech;
    char dialdevice[AST_CHANNEL_NAME];
    char *caller_id = NULL;
    char *cid_num = NULL;
    char *cid_name = NULL;
    int timeout = 30000;

    char *stuff;
    struct ast_channel *chan;
    RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);

    if (ast_strlen_zero(args->endpoint)) {
        ast_ari_response_error(response, 400, "Bad Request",
                               "Endpoint must be specified");
        return;
    }

    dialtech = ast_strdupa(args->endpoint);
    if ((stuff = strchr(dialtech, '/'))) {
        *stuff++ = '\0';
        ast_copy_string(dialdevice, stuff, sizeof(dialdevice));
    }

    if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) {
        ast_ari_response_error(response, 400, "Bad Request",
                               "Invalid endpoint specified");
        return;
    }

    if (args->timeout > 0) {
        timeout = args->timeout * 1000;
    } else if (args->timeout == -1) {
        timeout = -1;
    }

    if (!ast_strlen_zero(args->caller_id)) {
        caller_id = ast_strdupa(args->caller_id);
        ast_callerid_parse(caller_id, &cid_name, &cid_num);

        if (ast_is_shrinkable_phonenumber(cid_num)) {
            ast_shrink_phone_number(cid_num);
        }
    }

    if (!ast_strlen_zero(args->app)) {
        const char *app = "Stasis";

        RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free);

        if (!appdata) {
            ast_ari_response_alloc_failed(response);
            return;
        }

        ast_str_set(&appdata, 0, "%s", args->app);
        if (!ast_strlen_zero(args->app_args)) {
            ast_str_append(&appdata, 0, ",%s", args->app_args);
        }

        /* originate a channel, putting it into an application */
        if (ast_pbx_outgoing_app(dialtech, NULL, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, NULL, NULL, &chan)) {
            ast_ari_response_alloc_failed(response);
            return;
        }
    } else if (!ast_strlen_zero(args->extension)) {
Esempio n. 27
0
/*!
 * \brief Parameter parsing callback for /bridges/{bridgeId}/record.
 * \param get_params GET parameters in the HTTP request.
 * \param path_vars Path variables extracted from the request.
 * \param headers HTTP headers.
 * \param[out] response Response to the HTTP request.
 */
static void ast_ari_bridges_record_cb(
	struct ast_tcptls_session_instance *ser,
	struct ast_variable *get_params, struct ast_variable *path_vars,
	struct ast_variable *headers, struct ast_ari_response *response)
{
	struct ast_ari_bridges_record_args args = {};
	struct ast_variable *i;
	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
#if defined(AST_DEVMODE)
	int is_valid;
	int code;
#endif /* AST_DEVMODE */

	for (i = get_params; i; i = i->next) {
		if (strcmp(i->name, "name") == 0) {
			args.name = (i->value);
		} else
		if (strcmp(i->name, "format") == 0) {
			args.format = (i->value);
		} else
		if (strcmp(i->name, "maxDurationSeconds") == 0) {
			args.max_duration_seconds = atoi(i->value);
		} else
		if (strcmp(i->name, "maxSilenceSeconds") == 0) {
			args.max_silence_seconds = atoi(i->value);
		} else
		if (strcmp(i->name, "ifExists") == 0) {
			args.if_exists = (i->value);
		} else
		if (strcmp(i->name, "beep") == 0) {
			args.beep = ast_true(i->value);
		} else
		if (strcmp(i->name, "terminateOn") == 0) {
			args.terminate_on = (i->value);
		} else
		{}
	}
	for (i = path_vars; i; i = i->next) {
		if (strcmp(i->name, "bridgeId") == 0) {
			args.bridge_id = (i->value);
		} else
		{}
	}
	/* Look for a JSON request entity */
	body = ast_http_get_json(ser, headers);
	if (!body) {
		switch (errno) {
		case EFBIG:
			ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
			goto fin;
		case ENOMEM:
			ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
			goto fin;
		case EIO:
			ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
			goto fin;
		}
	}
	if (ast_ari_bridges_record_parse_body(body, &args)) {
		ast_ari_response_alloc_failed(response);
		goto fin;
	}
	ast_ari_bridges_record(headers, &args, response);
#if defined(AST_DEVMODE)
	code = response->response_code;

	switch (code) {
	case 0: /* Implementation is still a stub, or the code wasn't set */
		is_valid = response->message == NULL;
		break;
	case 500: /* Internal Server Error */
	case 501: /* Not Implemented */
	case 400: /* Invalid parameters */
	case 404: /* Bridge not found */
	case 409: /* Bridge is not in a Stasis application; A recording with the same name already exists on the system and can not be overwritten because it is in progress or ifExists=fail */
	case 422: /* The format specified is unknown on this system */
		is_valid = 1;
		break;
	default:
		if (200 <= code && code <= 299) {
			is_valid = ast_ari_validate_live_recording(
				response->message);
		} else {
			ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/record\n", code);
			is_valid = 0;
		}
	}

	if (!is_valid) {
		ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/record\n");
		ast_ari_response_error(response, 500,
			"Internal Server Error", "Response validation failed");
	}
#endif /* AST_DEVMODE */

fin: __attribute__((unused))
	return;
}
Esempio n. 28
0
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 ao2_iterator i;
	void *obj;

	if (!ast_get_channel_tech(args->tech)) {
		ast_ari_response_error(response, 404, "Not Found",
				       "No Endpoints found - invalid tech %s", args->tech);
		return;
	}

	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) {
			ast_ari_response_alloc_failed(response);
			return;
		}
	}
	ao2_iterator_destroy(&i);
	ast_ari_response_ok(response, ast_json_ref(json));
}
Esempio n. 29
0
/*!
 * \brief Parameter parsing callback for /bridges/{bridgeId}/play.
 * \param get_params GET parameters in the HTTP request.
 * \param path_vars Path variables extracted from the request.
 * \param headers HTTP headers.
 * \param[out] response Response to the HTTP request.
 */
static void ast_ari_bridges_play_cb(
	struct ast_tcptls_session_instance *ser,
	struct ast_variable *get_params, struct ast_variable *path_vars,
	struct ast_variable *headers, struct ast_ari_response *response)
{
	struct ast_ari_bridges_play_args args = {};
	struct ast_variable *i;
	RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
#if defined(AST_DEVMODE)
	int is_valid;
	int code;
#endif /* AST_DEVMODE */

	for (i = get_params; i; i = i->next) {
		if (strcmp(i->name, "media") == 0) {
			args.media = (i->value);
		} else
		if (strcmp(i->name, "lang") == 0) {
			args.lang = (i->value);
		} else
		if (strcmp(i->name, "offsetms") == 0) {
			args.offsetms = atoi(i->value);
		} else
		if (strcmp(i->name, "skipms") == 0) {
			args.skipms = atoi(i->value);
		} else
		if (strcmp(i->name, "playbackId") == 0) {
			args.playback_id = (i->value);
		} else
		{}
	}
	for (i = path_vars; i; i = i->next) {
		if (strcmp(i->name, "bridgeId") == 0) {
			args.bridge_id = (i->value);
		} else
		{}
	}
	/* Look for a JSON request entity */
	body = ast_http_get_json(ser, headers);
	if (!body) {
		switch (errno) {
		case EFBIG:
			ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
			goto fin;
		case ENOMEM:
			ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
			goto fin;
		case EIO:
			ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
			goto fin;
		}
	}
	if (ast_ari_bridges_play_parse_body(body, &args)) {
		ast_ari_response_alloc_failed(response);
		goto fin;
	}
	ast_ari_bridges_play(headers, &args, response);
#if defined(AST_DEVMODE)
	code = response->response_code;

	switch (code) {
	case 0: /* Implementation is still a stub, or the code wasn't set */
		is_valid = response->message == NULL;
		break;
	case 500: /* Internal Server Error */
	case 501: /* Not Implemented */
	case 404: /* Bridge not found */
	case 409: /* Bridge not in a Stasis application */
		is_valid = 1;
		break;
	default:
		if (200 <= code && code <= 299) {
			is_valid = ast_ari_validate_playback(
				response->message);
		} else {
			ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/play\n", code);
			is_valid = 0;
		}
	}

	if (!is_valid) {
		ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/play\n");
		ast_ari_response_error(response, 500,
			"Internal Server Error", "Response validation failed");
	}
#endif /* AST_DEVMODE */

fin: __attribute__((unused))
	return;
}
Esempio n. 30
0
/*!
 * \brief Handle OPTIONS request, mainly for CORS preflight requests.
 *
 * Some browsers will send this prior to non-simple methods (i.e. DELETE).
 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
 */
static void handle_options(struct stasis_rest_handlers *handler,
                           struct ast_variable *headers,
                           struct ast_ari_response *response)
{
    struct ast_variable *header;
    char const *acr_method = NULL;
    char const *acr_headers = NULL;
    char const *origin = NULL;

    RAII_VAR(struct ast_str *, allow, NULL, ast_free);
    enum ast_http_method m;
    int allowed = 0;

    /* Regular OPTIONS response */
    add_allow_header(handler, response);
    ast_ari_response_no_content(response);

    /* Parse CORS headers */
    for (header = headers; header != NULL; header = header->next) {
        if (strcmp(ACR_METHOD, header->name) == 0) {
            acr_method = header->value;
        } else if (strcmp(ACR_HEADERS, header->name) == 0) {
            acr_headers = header->value;
        } else if (strcmp("Origin", header->name) == 0) {
            origin = header->value;
        }
    }

    /* CORS 6.2, #1 - "If the Origin header is not present terminate this
     * set of steps."
     */
    if (origin == NULL) {
        return;
    }

    /* CORS 6.2, #2 - "If the value of the Origin header is not a
     * case-sensitive match for any of the values in list of origins do not
     * set any additional headers and terminate this set of steps.
     *
     * Always matching is acceptable since the list of origins can be
     * unbounded.
     *
     * The Origin header can only contain a single origin as the user agent
     * will not follow redirects."
     */
    if (!origin_allowed(origin)) {
        ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
        return;
    }

    /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
     * or if parsing failed, do not set any additional headers and terminate
     * this set of steps."
     */
    if (acr_method == NULL) {
        return;
    }

    /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
     * headers let header field-names be the empty list."
     */
    if (acr_headers == NULL) {
        acr_headers = "";
    }

    /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
     * the values in list of methods do not set any additional headers and
     * terminate this set of steps."
     */
    allow = ast_str_create(20);

    if (!allow) {
        ast_ari_response_alloc_failed(response);
        return;
    }

    /* Go ahead and build the ACA_METHODS header at the same time */
    for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
        if (handler->callbacks[m] != NULL) {
            char const *m_str = ast_get_http_method(m);
            if (strcmp(m_str, acr_method) == 0) {
                allowed = 1;
            }
            ast_str_append(&allow, 0, ",%s", m_str);
        }
    }

    if (!allowed) {
        return;
    }

    /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
     * case-insensitive match for any of the values in list of headers do
     * not set any additional headers and terminate this set of steps.
     *
     * Note: Always matching is acceptable since the list of headers can be
     * unbounded."
     */

    /* CORS 6.2 #7 - "If the resource supports credentials add a single
     * Access-Control-Allow-Origin header, with the value of the Origin
     * header as value, and add a single Access-Control-Allow-Credentials
     * header with the case-sensitive string "true" as value."
     *
     * Added by process_cors_request() earlier in the request.
     */

    /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
     * header..."
     */

    /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
     * consisting of (a subset of) the list of methods."
     */
    ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
                   ACA_METHODS, ast_str_buffer(allow));


    /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
     * consisting of (a subset of) the list of headers.
     *
     * Since the list of headers can be unbounded simply returning headers
     * can be enough."
     */
    if (!ast_strlen_zero(acr_headers)) {
        ast_str_append(&response->headers, 0, "%s: %s\r\n",
                       ACA_HEADERS, acr_headers);
    }
}