diff options
Diffstat (limited to 'res/ari')
-rw-r--r-- | res/ari/ari_model_validators.c | 2969 | ||||
-rw-r--r-- | res/ari/ari_model_validators.h | 962 | ||||
-rw-r--r-- | res/ari/ari_websockets.c | 165 | ||||
-rw-r--r-- | res/ari/cli.c | 267 | ||||
-rw-r--r-- | res/ari/config.c | 345 | ||||
-rw-r--r-- | res/ari/internal.h | 143 | ||||
-rw-r--r-- | res/ari/resource_asterisk.c | 80 | ||||
-rw-r--r-- | res/ari/resource_asterisk.h | 84 | ||||
-rw-r--r-- | res/ari/resource_bridges.c | 514 | ||||
-rw-r--r-- | res/ari/resource_bridges.h | 179 | ||||
-rw-r--r-- | res/ari/resource_channels.c | 693 | ||||
-rw-r--r-- | res/ari/resource_channels.h | 330 | ||||
-rw-r--r-- | res/ari/resource_endpoints.c | 157 | ||||
-rw-r--r-- | res/ari/resource_endpoints.h | 82 | ||||
-rw-r--r-- | res/ari/resource_events.c | 218 | ||||
-rw-r--r-- | res/ari/resource_events.h | 56 | ||||
-rw-r--r-- | res/ari/resource_playback.c | 137 | ||||
-rw-r--r-- | res/ari/resource_playback.h | 84 | ||||
-rw-r--r-- | res/ari/resource_recordings.c | 97 | ||||
-rw-r--r-- | res/ari/resource_recordings.h | 186 | ||||
-rw-r--r-- | res/ari/resource_sounds.c | 220 | ||||
-rw-r--r-- | res/ari/resource_sounds.h | 69 |
22 files changed, 8037 insertions, 0 deletions
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c new file mode 100644 index 000000000..1894ddb9a --- /dev/null +++ b/res/ari/ari_model_validators.c @@ -0,0 +1,2969 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * 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 Generated file - Build validators for ARI model objects. + */ + + /* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_model_validators.h.mustache + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/logger.h" +#include "asterisk/module.h" +#include "ari_model_validators.h" + +int ast_ari_validate_asterisk_info(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + { + ast_log(LOG_ERROR, + "ARI AsteriskInfo has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + return res; +} + +ari_validator ast_ari_validate_asterisk_info_fn(void) +{ + return ast_ari_validate_asterisk_info; +} + +int ast_ari_validate_variable(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_value = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("value", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_value = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Variable field value failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Variable has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_value) { + ast_log(LOG_ERROR, "ARI Variable missing required field value\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_variable_fn(void) +{ + return ast_ari_validate_variable; +} + +int ast_ari_validate_endpoint(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_channel_ids = 0; + int has_resource = 0; + int has_technology = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("channel_ids", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel_ids = 1; + prop_is_valid = ast_ari_validate_list( + ast_json_object_iter_value(iter), + ast_ari_validate_string); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Endpoint field channel_ids failed validation\n"); + res = 0; + } + } else + if (strcmp("resource", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_resource = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Endpoint field resource failed validation\n"); + res = 0; + } + } else + if (strcmp("state", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Endpoint field state failed validation\n"); + res = 0; + } + } else + if (strcmp("technology", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_technology = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Endpoint field technology failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Endpoint has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_channel_ids) { + ast_log(LOG_ERROR, "ARI Endpoint missing required field channel_ids\n"); + res = 0; + } + + if (!has_resource) { + ast_log(LOG_ERROR, "ARI Endpoint missing required field resource\n"); + res = 0; + } + + if (!has_technology) { + ast_log(LOG_ERROR, "ARI Endpoint missing required field technology\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_endpoint_fn(void) +{ + return ast_ari_validate_endpoint; +} + +int ast_ari_validate_caller_id(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_name = 0; + int has_number = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("name", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_name = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI CallerID field name failed validation\n"); + res = 0; + } + } else + if (strcmp("number", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_number = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI CallerID field number failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI CallerID has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_name) { + ast_log(LOG_ERROR, "ARI CallerID missing required field name\n"); + res = 0; + } + + if (!has_number) { + ast_log(LOG_ERROR, "ARI CallerID missing required field number\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_caller_id_fn(void) +{ + return ast_ari_validate_caller_id; +} + +int ast_ari_validate_channel(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_accountcode = 0; + int has_caller = 0; + int has_connected = 0; + int has_creationtime = 0; + int has_dialplan = 0; + int has_id = 0; + int has_name = 0; + int has_state = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("accountcode", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_accountcode = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field accountcode failed validation\n"); + res = 0; + } + } else + if (strcmp("caller", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_caller = 1; + prop_is_valid = ast_ari_validate_caller_id( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field caller failed validation\n"); + res = 0; + } + } else + if (strcmp("connected", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_connected = 1; + prop_is_valid = ast_ari_validate_caller_id( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field connected failed validation\n"); + res = 0; + } + } else + if (strcmp("creationtime", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_creationtime = 1; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field creationtime failed validation\n"); + res = 0; + } + } else + if (strcmp("dialplan", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_dialplan = 1; + prop_is_valid = ast_ari_validate_dialplan_cep( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field dialplan failed validation\n"); + res = 0; + } + } else + if (strcmp("id", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_id = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field id failed validation\n"); + res = 0; + } + } else + if (strcmp("name", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_name = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field name failed validation\n"); + res = 0; + } + } else + if (strcmp("state", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_state = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field state failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Channel has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_accountcode) { + ast_log(LOG_ERROR, "ARI Channel missing required field accountcode\n"); + res = 0; + } + + if (!has_caller) { + ast_log(LOG_ERROR, "ARI Channel missing required field caller\n"); + res = 0; + } + + if (!has_connected) { + ast_log(LOG_ERROR, "ARI Channel missing required field connected\n"); + res = 0; + } + + if (!has_creationtime) { + ast_log(LOG_ERROR, "ARI Channel missing required field creationtime\n"); + res = 0; + } + + if (!has_dialplan) { + ast_log(LOG_ERROR, "ARI Channel missing required field dialplan\n"); + res = 0; + } + + if (!has_id) { + ast_log(LOG_ERROR, "ARI Channel missing required field id\n"); + res = 0; + } + + if (!has_name) { + ast_log(LOG_ERROR, "ARI Channel missing required field name\n"); + res = 0; + } + + if (!has_state) { + ast_log(LOG_ERROR, "ARI Channel missing required field state\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_fn(void) +{ + return ast_ari_validate_channel; +} + +int ast_ari_validate_dialed(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + { + ast_log(LOG_ERROR, + "ARI Dialed has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + return res; +} + +ari_validator ast_ari_validate_dialed_fn(void) +{ + return ast_ari_validate_dialed; +} + +int ast_ari_validate_dialplan_cep(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_context = 0; + int has_exten = 0; + int has_priority = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("context", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_context = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI DialplanCEP field context failed validation\n"); + res = 0; + } + } else + if (strcmp("exten", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_exten = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI DialplanCEP field exten failed validation\n"); + res = 0; + } + } else + if (strcmp("priority", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_priority = 1; + prop_is_valid = ast_ari_validate_long( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI DialplanCEP field priority failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI DialplanCEP has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_context) { + ast_log(LOG_ERROR, "ARI DialplanCEP missing required field context\n"); + res = 0; + } + + if (!has_exten) { + ast_log(LOG_ERROR, "ARI DialplanCEP missing required field exten\n"); + res = 0; + } + + if (!has_priority) { + ast_log(LOG_ERROR, "ARI DialplanCEP missing required field priority\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_dialplan_cep_fn(void) +{ + return ast_ari_validate_dialplan_cep; +} + +int ast_ari_validate_bridge(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_bridge_class = 0; + int has_bridge_type = 0; + int has_channels = 0; + int has_id = 0; + int has_technology = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("bridge_class", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_bridge_class = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Bridge field bridge_class failed validation\n"); + res = 0; + } + } else + if (strcmp("bridge_type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_bridge_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Bridge field bridge_type failed validation\n"); + res = 0; + } + } else + if (strcmp("channels", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channels = 1; + prop_is_valid = ast_ari_validate_list( + ast_json_object_iter_value(iter), + ast_ari_validate_string); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Bridge field channels failed validation\n"); + res = 0; + } + } else + if (strcmp("id", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_id = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Bridge field id failed validation\n"); + res = 0; + } + } else + if (strcmp("technology", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_technology = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Bridge field technology failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Bridge has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_bridge_class) { + ast_log(LOG_ERROR, "ARI Bridge missing required field bridge_class\n"); + res = 0; + } + + if (!has_bridge_type) { + ast_log(LOG_ERROR, "ARI Bridge missing required field bridge_type\n"); + res = 0; + } + + if (!has_channels) { + ast_log(LOG_ERROR, "ARI Bridge missing required field channels\n"); + res = 0; + } + + if (!has_id) { + ast_log(LOG_ERROR, "ARI Bridge missing required field id\n"); + res = 0; + } + + if (!has_technology) { + ast_log(LOG_ERROR, "ARI Bridge missing required field technology\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_bridge_fn(void) +{ + return ast_ari_validate_bridge; +} + +int ast_ari_validate_live_recording(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_format = 0; + int has_name = 0; + int has_state = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("format", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_format = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LiveRecording field format failed validation\n"); + res = 0; + } + } else + if (strcmp("name", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_name = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LiveRecording field name failed validation\n"); + res = 0; + } + } else + if (strcmp("state", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_state = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LiveRecording field state failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI LiveRecording has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_format) { + ast_log(LOG_ERROR, "ARI LiveRecording missing required field format\n"); + res = 0; + } + + if (!has_name) { + ast_log(LOG_ERROR, "ARI LiveRecording missing required field name\n"); + res = 0; + } + + if (!has_state) { + ast_log(LOG_ERROR, "ARI LiveRecording missing required field state\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_live_recording_fn(void) +{ + return ast_ari_validate_live_recording; +} + +int ast_ari_validate_stored_recording(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_formats = 0; + int has_id = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("duration_seconds", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_int( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StoredRecording field duration_seconds failed validation\n"); + res = 0; + } + } else + if (strcmp("formats", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_formats = 1; + prop_is_valid = ast_ari_validate_list( + ast_json_object_iter_value(iter), + ast_ari_validate_string); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StoredRecording field formats failed validation\n"); + res = 0; + } + } else + if (strcmp("id", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_id = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StoredRecording field id failed validation\n"); + res = 0; + } + } else + if (strcmp("time", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StoredRecording field time failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI StoredRecording has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_formats) { + ast_log(LOG_ERROR, "ARI StoredRecording missing required field formats\n"); + res = 0; + } + + if (!has_id) { + ast_log(LOG_ERROR, "ARI StoredRecording missing required field id\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_stored_recording_fn(void) +{ + return ast_ari_validate_stored_recording; +} + +int ast_ari_validate_format_lang_pair(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_format = 0; + int has_language = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("format", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_format = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI FormatLangPair field format failed validation\n"); + res = 0; + } + } else + if (strcmp("language", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_language = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI FormatLangPair field language failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI FormatLangPair has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_format) { + ast_log(LOG_ERROR, "ARI FormatLangPair missing required field format\n"); + res = 0; + } + + if (!has_language) { + ast_log(LOG_ERROR, "ARI FormatLangPair missing required field language\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_format_lang_pair_fn(void) +{ + return ast_ari_validate_format_lang_pair; +} + +int ast_ari_validate_sound(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_formats = 0; + int has_id = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("formats", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_formats = 1; + prop_is_valid = ast_ari_validate_list( + ast_json_object_iter_value(iter), + ast_ari_validate_format_lang_pair); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Sound field formats failed validation\n"); + res = 0; + } + } else + if (strcmp("id", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_id = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Sound field id failed validation\n"); + res = 0; + } + } else + if (strcmp("text", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Sound field text failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Sound has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_formats) { + ast_log(LOG_ERROR, "ARI Sound missing required field formats\n"); + res = 0; + } + + if (!has_id) { + ast_log(LOG_ERROR, "ARI Sound missing required field id\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_sound_fn(void) +{ + return ast_ari_validate_sound; +} + +int ast_ari_validate_playback(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_id = 0; + int has_media_uri = 0; + int has_state = 0; + int has_target_uri = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("id", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_id = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Playback field id failed validation\n"); + res = 0; + } + } else + if (strcmp("language", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Playback field language failed validation\n"); + res = 0; + } + } else + if (strcmp("media_uri", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_media_uri = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Playback field media_uri failed validation\n"); + res = 0; + } + } else + if (strcmp("state", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_state = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Playback field state failed validation\n"); + res = 0; + } + } else + if (strcmp("target_uri", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_target_uri = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Playback field target_uri failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Playback has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_id) { + ast_log(LOG_ERROR, "ARI Playback missing required field id\n"); + res = 0; + } + + if (!has_media_uri) { + ast_log(LOG_ERROR, "ARI Playback missing required field media_uri\n"); + res = 0; + } + + if (!has_state) { + ast_log(LOG_ERROR, "ARI Playback missing required field state\n"); + res = 0; + } + + if (!has_target_uri) { + ast_log(LOG_ERROR, "ARI Playback missing required field target_uri\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_playback_fn(void) +{ + return ast_ari_validate_playback; +} + +int ast_ari_validate_application_replaced(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ApplicationReplaced field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ApplicationReplaced field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ApplicationReplaced field timestamp failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ApplicationReplaced has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ApplicationReplaced missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ApplicationReplaced missing required field application\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_application_replaced_fn(void) +{ + return ast_ari_validate_application_replaced; +} + +int ast_ari_validate_bridge_created(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_bridge = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeCreated field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeCreated field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeCreated field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_bridge = 1; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeCreated field bridge failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI BridgeCreated has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI BridgeCreated missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI BridgeCreated missing required field application\n"); + res = 0; + } + + if (!has_bridge) { + ast_log(LOG_ERROR, "ARI BridgeCreated missing required field bridge\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_bridge_created_fn(void) +{ + return ast_ari_validate_bridge_created; +} + +int ast_ari_validate_bridge_destroyed(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_bridge = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeDestroyed field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeDestroyed field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeDestroyed field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_bridge = 1; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeDestroyed field bridge failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI BridgeDestroyed has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI BridgeDestroyed missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI BridgeDestroyed missing required field application\n"); + res = 0; + } + + if (!has_bridge) { + ast_log(LOG_ERROR, "ARI BridgeDestroyed missing required field bridge\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_bridge_destroyed_fn(void) +{ + return ast_ari_validate_bridge_destroyed; +} + +int ast_ari_validate_bridge_merged(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_bridge = 0; + int has_bridge_from = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeMerged field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeMerged field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeMerged field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_bridge = 1; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeMerged field bridge failed validation\n"); + res = 0; + } + } else + if (strcmp("bridge_from", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_bridge_from = 1; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI BridgeMerged field bridge_from failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI BridgeMerged has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI BridgeMerged missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI BridgeMerged missing required field application\n"); + res = 0; + } + + if (!has_bridge) { + ast_log(LOG_ERROR, "ARI BridgeMerged missing required field bridge\n"); + res = 0; + } + + if (!has_bridge_from) { + ast_log(LOG_ERROR, "ARI BridgeMerged missing required field bridge_from\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_bridge_merged_fn(void) +{ + return ast_ari_validate_bridge_merged; +} + +int ast_ari_validate_channel_caller_id(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_caller_presentation = 0; + int has_caller_presentation_txt = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCallerId field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCallerId field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCallerId field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("caller_presentation", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_caller_presentation = 1; + prop_is_valid = ast_ari_validate_int( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCallerId field caller_presentation failed validation\n"); + res = 0; + } + } else + if (strcmp("caller_presentation_txt", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_caller_presentation_txt = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCallerId field caller_presentation_txt failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCallerId field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelCallerId has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field application\n"); + res = 0; + } + + if (!has_caller_presentation) { + ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field caller_presentation\n"); + res = 0; + } + + if (!has_caller_presentation_txt) { + ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field caller_presentation_txt\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_caller_id_fn(void) +{ + return ast_ari_validate_channel_caller_id; +} + +int ast_ari_validate_channel_created(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCreated field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCreated field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCreated field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelCreated field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelCreated has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelCreated missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelCreated missing required field application\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelCreated missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_created_fn(void) +{ + return ast_ari_validate_channel_created; +} + +int ast_ari_validate_channel_destroyed(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_cause = 0; + int has_cause_txt = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("cause", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_cause = 1; + prop_is_valid = ast_ari_validate_int( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed field cause failed validation\n"); + res = 0; + } + } else + if (strcmp("cause_txt", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_cause_txt = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed field cause_txt failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelDestroyed has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field application\n"); + res = 0; + } + + if (!has_cause) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field cause\n"); + res = 0; + } + + if (!has_cause_txt) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field cause_txt\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_destroyed_fn(void) +{ + return ast_ari_validate_channel_destroyed; +} + +int ast_ari_validate_channel_dialplan(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_channel = 0; + int has_dialplan_app = 0; + int has_dialplan_app_data = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDialplan field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDialplan field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDialplan field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDialplan field channel failed validation\n"); + res = 0; + } + } else + if (strcmp("dialplan_app", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_dialplan_app = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDialplan field dialplan_app failed validation\n"); + res = 0; + } + } else + if (strcmp("dialplan_app_data", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_dialplan_app_data = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDialplan field dialplan_app_data failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelDialplan has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field application\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field channel\n"); + res = 0; + } + + if (!has_dialplan_app) { + ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field dialplan_app\n"); + res = 0; + } + + if (!has_dialplan_app_data) { + ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field dialplan_app_data\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_dialplan_fn(void) +{ + return ast_ari_validate_channel_dialplan; +} + +int ast_ari_validate_channel_dtmf_received(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_channel = 0; + int has_digit = 0; + int has_duration_ms = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field channel failed validation\n"); + res = 0; + } + } else + if (strcmp("digit", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_digit = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field digit failed validation\n"); + res = 0; + } + } else + if (strcmp("duration_ms", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_duration_ms = 1; + prop_is_valid = ast_ari_validate_int( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field duration_ms failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelDtmfReceived has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field application\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field channel\n"); + res = 0; + } + + if (!has_digit) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field digit\n"); + res = 0; + } + + if (!has_duration_ms) { + ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field duration_ms\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_dtmf_received_fn(void) +{ + return ast_ari_validate_channel_dtmf_received; +} + +int ast_ari_validate_channel_entered_bridge(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_bridge = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_bridge = 1; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field bridge failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelEnteredBridge has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelEnteredBridge missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelEnteredBridge missing required field application\n"); + res = 0; + } + + if (!has_bridge) { + ast_log(LOG_ERROR, "ARI ChannelEnteredBridge missing required field bridge\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_entered_bridge_fn(void) +{ + return ast_ari_validate_channel_entered_bridge; +} + +int ast_ari_validate_channel_hangup_request(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("cause", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_int( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest field cause failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest field channel failed validation\n"); + res = 0; + } + } else + if (strcmp("soft", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_boolean( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest field soft failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelHangupRequest has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest missing required field application\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelHangupRequest missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_hangup_request_fn(void) +{ + return ast_ari_validate_channel_hangup_request; +} + +int ast_ari_validate_channel_left_bridge(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_bridge = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_bridge = 1; + prop_is_valid = ast_ari_validate_bridge( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge field bridge failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelLeftBridge has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field application\n"); + res = 0; + } + + if (!has_bridge) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field bridge\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_left_bridge_fn(void) +{ + return ast_ari_validate_channel_left_bridge; +} + +int ast_ari_validate_channel_state_change(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelStateChange field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelStateChange field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelStateChange field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelStateChange field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelStateChange has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field application\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_state_change_fn(void) +{ + return ast_ari_validate_channel_state_change; +} + +int ast_ari_validate_channel_userevent(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_channel = 0; + int has_eventname = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelUserevent field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelUserevent field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelUserevent field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelUserevent field channel failed validation\n"); + res = 0; + } + } else + if (strcmp("eventname", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_eventname = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelUserevent field eventname failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelUserevent has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelUserevent missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelUserevent missing required field application\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI ChannelUserevent missing required field channel\n"); + res = 0; + } + + if (!has_eventname) { + ast_log(LOG_ERROR, "ARI ChannelUserevent missing required field eventname\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_userevent_fn(void) +{ + return ast_ari_validate_channel_userevent; +} + +int ast_ari_validate_channel_varset(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_value = 0; + int has_variable = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelVarset field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelVarset field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelVarset field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelVarset field channel failed validation\n"); + res = 0; + } + } else + if (strcmp("value", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_value = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelVarset field value failed validation\n"); + res = 0; + } + } else + if (strcmp("variable", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_variable = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI ChannelVarset field variable failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI ChannelVarset has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI ChannelVarset missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI ChannelVarset missing required field application\n"); + res = 0; + } + + if (!has_value) { + ast_log(LOG_ERROR, "ARI ChannelVarset missing required field value\n"); + res = 0; + } + + if (!has_variable) { + ast_log(LOG_ERROR, "ARI ChannelVarset missing required field variable\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_channel_varset_fn(void) +{ + return ast_ari_validate_channel_varset; +} + +int ast_ari_validate_event(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + const char *discriminator; + + discriminator = ast_json_string_get(ast_json_object_get(json, "type")); + if (!discriminator) { + ast_log(LOG_ERROR, "ARI Event missing required field type"); + return 0; + } + + if (strcmp("Event", discriminator) == 0) { + /* Self type; fall through */ + } else + if (strcmp("ApplicationReplaced", discriminator) == 0) { + return ast_ari_validate_application_replaced(json); + } else + if (strcmp("BridgeCreated", discriminator) == 0) { + return ast_ari_validate_bridge_created(json); + } else + if (strcmp("BridgeDestroyed", discriminator) == 0) { + return ast_ari_validate_bridge_destroyed(json); + } else + if (strcmp("BridgeMerged", discriminator) == 0) { + return ast_ari_validate_bridge_merged(json); + } else + if (strcmp("ChannelCallerId", discriminator) == 0) { + return ast_ari_validate_channel_caller_id(json); + } else + if (strcmp("ChannelCreated", discriminator) == 0) { + return ast_ari_validate_channel_created(json); + } else + if (strcmp("ChannelDestroyed", discriminator) == 0) { + return ast_ari_validate_channel_destroyed(json); + } else + if (strcmp("ChannelDialplan", discriminator) == 0) { + return ast_ari_validate_channel_dialplan(json); + } else + if (strcmp("ChannelDtmfReceived", discriminator) == 0) { + return ast_ari_validate_channel_dtmf_received(json); + } else + if (strcmp("ChannelEnteredBridge", discriminator) == 0) { + return ast_ari_validate_channel_entered_bridge(json); + } else + if (strcmp("ChannelHangupRequest", discriminator) == 0) { + return ast_ari_validate_channel_hangup_request(json); + } else + if (strcmp("ChannelLeftBridge", discriminator) == 0) { + return ast_ari_validate_channel_left_bridge(json); + } else + if (strcmp("ChannelStateChange", discriminator) == 0) { + return ast_ari_validate_channel_state_change(json); + } else + if (strcmp("ChannelUserevent", discriminator) == 0) { + return ast_ari_validate_channel_userevent(json); + } else + if (strcmp("ChannelVarset", discriminator) == 0) { + return ast_ari_validate_channel_varset(json); + } else + if (strcmp("PlaybackFinished", discriminator) == 0) { + return ast_ari_validate_playback_finished(json); + } else + if (strcmp("PlaybackStarted", discriminator) == 0) { + return ast_ari_validate_playback_started(json); + } else + if (strcmp("StasisEnd", discriminator) == 0) { + return ast_ari_validate_stasis_end(json); + } else + if (strcmp("StasisStart", discriminator) == 0) { + return ast_ari_validate_stasis_start(json); + } else + { + ast_log(LOG_ERROR, "ARI Event has undocumented subtype %s\n", + discriminator); + res = 0; + } + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Event field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Event field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Event field timestamp failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Event has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI Event missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI Event missing required field application\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_event_fn(void) +{ + return ast_ari_validate_event; +} + +int ast_ari_validate_message(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + const char *discriminator; + + discriminator = ast_json_string_get(ast_json_object_get(json, "type")); + if (!discriminator) { + ast_log(LOG_ERROR, "ARI Message missing required field type"); + return 0; + } + + if (strcmp("Message", discriminator) == 0) { + /* Self type; fall through */ + } else + if (strcmp("ApplicationReplaced", discriminator) == 0) { + return ast_ari_validate_application_replaced(json); + } else + if (strcmp("BridgeCreated", discriminator) == 0) { + return ast_ari_validate_bridge_created(json); + } else + if (strcmp("BridgeDestroyed", discriminator) == 0) { + return ast_ari_validate_bridge_destroyed(json); + } else + if (strcmp("BridgeMerged", discriminator) == 0) { + return ast_ari_validate_bridge_merged(json); + } else + if (strcmp("ChannelCallerId", discriminator) == 0) { + return ast_ari_validate_channel_caller_id(json); + } else + if (strcmp("ChannelCreated", discriminator) == 0) { + return ast_ari_validate_channel_created(json); + } else + if (strcmp("ChannelDestroyed", discriminator) == 0) { + return ast_ari_validate_channel_destroyed(json); + } else + if (strcmp("ChannelDialplan", discriminator) == 0) { + return ast_ari_validate_channel_dialplan(json); + } else + if (strcmp("ChannelDtmfReceived", discriminator) == 0) { + return ast_ari_validate_channel_dtmf_received(json); + } else + if (strcmp("ChannelEnteredBridge", discriminator) == 0) { + return ast_ari_validate_channel_entered_bridge(json); + } else + if (strcmp("ChannelHangupRequest", discriminator) == 0) { + return ast_ari_validate_channel_hangup_request(json); + } else + if (strcmp("ChannelLeftBridge", discriminator) == 0) { + return ast_ari_validate_channel_left_bridge(json); + } else + if (strcmp("ChannelStateChange", discriminator) == 0) { + return ast_ari_validate_channel_state_change(json); + } else + if (strcmp("ChannelUserevent", discriminator) == 0) { + return ast_ari_validate_channel_userevent(json); + } else + if (strcmp("ChannelVarset", discriminator) == 0) { + return ast_ari_validate_channel_varset(json); + } else + if (strcmp("Event", discriminator) == 0) { + return ast_ari_validate_event(json); + } else + if (strcmp("MissingParams", discriminator) == 0) { + return ast_ari_validate_missing_params(json); + } else + if (strcmp("PlaybackFinished", discriminator) == 0) { + return ast_ari_validate_playback_finished(json); + } else + if (strcmp("PlaybackStarted", discriminator) == 0) { + return ast_ari_validate_playback_started(json); + } else + if (strcmp("StasisEnd", discriminator) == 0) { + return ast_ari_validate_stasis_end(json); + } else + if (strcmp("StasisStart", discriminator) == 0) { + return ast_ari_validate_stasis_start(json); + } else + { + ast_log(LOG_ERROR, "ARI Message has undocumented subtype %s\n", + discriminator); + res = 0; + } + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Message field type failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI Message has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI Message missing required field type\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_message_fn(void) +{ + return ast_ari_validate_message; +} + +int ast_ari_validate_missing_params(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_params = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI MissingParams field type failed validation\n"); + res = 0; + } + } else + if (strcmp("params", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_params = 1; + prop_is_valid = ast_ari_validate_list( + ast_json_object_iter_value(iter), + ast_ari_validate_string); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI MissingParams field params failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI MissingParams has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI MissingParams missing required field type\n"); + res = 0; + } + + if (!has_params) { + ast_log(LOG_ERROR, "ARI MissingParams missing required field params\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_missing_params_fn(void) +{ + return ast_ari_validate_missing_params; +} + +int ast_ari_validate_playback_finished(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_playback = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI PlaybackFinished field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI PlaybackFinished field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI PlaybackFinished field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("playback", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_playback = 1; + prop_is_valid = ast_ari_validate_playback( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI PlaybackFinished field playback failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI PlaybackFinished has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI PlaybackFinished missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI PlaybackFinished missing required field application\n"); + res = 0; + } + + if (!has_playback) { + ast_log(LOG_ERROR, "ARI PlaybackFinished missing required field playback\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_playback_finished_fn(void) +{ + return ast_ari_validate_playback_finished; +} + +int ast_ari_validate_playback_started(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_playback = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI PlaybackStarted field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI PlaybackStarted field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI PlaybackStarted field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("playback", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_playback = 1; + prop_is_valid = ast_ari_validate_playback( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI PlaybackStarted field playback failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI PlaybackStarted has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI PlaybackStarted missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI PlaybackStarted missing required field application\n"); + res = 0; + } + + if (!has_playback) { + ast_log(LOG_ERROR, "ARI PlaybackStarted missing required field playback\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_playback_started_fn(void) +{ + return ast_ari_validate_playback_started; +} + +int ast_ari_validate_stasis_end(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisEnd field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisEnd field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisEnd field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisEnd field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI StasisEnd has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI StasisEnd missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI StasisEnd missing required field application\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI StasisEnd missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_stasis_end_fn(void) +{ + return ast_ari_validate_stasis_end; +} + +int ast_ari_validate_stasis_start(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; + int has_type = 0; + int has_application = 0; + int has_args = 0; + int has_channel = 0; + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("type", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_type = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisStart field type failed validation\n"); + res = 0; + } + } else + if (strcmp("application", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_application = 1; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisStart field application failed validation\n"); + res = 0; + } + } else + if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_date( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisStart field timestamp failed validation\n"); + res = 0; + } + } else + if (strcmp("args", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_args = 1; + prop_is_valid = ast_ari_validate_list( + ast_json_object_iter_value(iter), + ast_ari_validate_string); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisStart field args failed validation\n"); + res = 0; + } + } else + if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_channel = 1; + prop_is_valid = ast_ari_validate_channel( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI StasisStart field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI StasisStart has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI StasisStart missing required field type\n"); + res = 0; + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI StasisStart missing required field application\n"); + res = 0; + } + + if (!has_args) { + ast_log(LOG_ERROR, "ARI StasisStart missing required field args\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI StasisStart missing required field channel\n"); + res = 0; + } + + return res; +} + +ari_validator ast_ari_validate_stasis_start_fn(void) +{ + return ast_ari_validate_stasis_start; +} diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h new file mode 100644 index 000000000..c5e74d21a --- /dev/null +++ b/res/ari/ari_model_validators.h @@ -0,0 +1,962 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * 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 Generated file - Build validators for ARI model objects. + * + * In addition to the normal validation functions one would normally expect, + * each validator has a ast_ari_validate_{id}_fn() companion function that returns + * the validator's function pointer. + * + * The reason for this seamingly useless indirection is the way function + * pointers interfere with module loading. Asterisk attempts to dlopen() each + * module using \c RTLD_LAZY in order to read some metadata from the module. + * Unfortunately, if you take the address of a function, the function has to be + * resolvable at load time, even if \c RTLD_LAZY is specified. By moving the + * function-address-taking into this module, we can once again be lazy. + */ + + /* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_model_validators.h.mustache + */ + +#ifndef _ASTERISK_ARI_MODEL_H +#define _ASTERISK_ARI_MODEL_H + +#include "asterisk/json.h" + +/*! @{ */ + +/*! + * \brief Validator for native Swagger void. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_void(struct ast_json *json); + +/*! + * \brief Validator for native Swagger byte. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_byte(struct ast_json *json); + +/*! + * \brief Validator for native Swagger boolean. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_boolean(struct ast_json *json); + +/*! + * \brief Validator for native Swagger int. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_int(struct ast_json *json); + +/*! + * \brief Validator for native Swagger long. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_long(struct ast_json *json); + +/*! + * \brief Validator for native Swagger float. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_float(struct ast_json *json); + +/*! + * \brief Validator for native Swagger double. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_double(struct ast_json *json); + +/*! + * \brief Validator for native Swagger string. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_string(struct ast_json *json); + +/*! + * \brief Validator for native Swagger date. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_date(struct ast_json *json); + +/*! + * \brief Validator for a Swagger List[]/JSON array. + * + * \param json JSON object to validate. + * \param fn Validator to call on every element in the array. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_list(struct ast_json *json, int (*fn)(struct ast_json *)); + +/*! @} */ + +/*! + * \brief Function type for validator functions. Allows for + */ +typedef int (*ari_validator)(struct ast_json *json); + +/*! + * \brief Validator for AsteriskInfo. + * + * Asterisk system information + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_asterisk_info(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_asterisk_info(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_asterisk_info_fn(void); + +/*! + * \brief Validator for Variable. + * + * The value of a channel variable + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_variable(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_variable(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_variable_fn(void); + +/*! + * \brief Validator for Endpoint. + * + * An external device that may offer/accept calls to/from Asterisk. + * + * Unlike most resources, which have a single unique identifier, an endpoint is uniquely identified by the technology/resource pair. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_endpoint(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_endpoint(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_endpoint_fn(void); + +/*! + * \brief Validator for CallerID. + * + * Caller identification + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_caller_id(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_caller_id(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_caller_id_fn(void); + +/*! + * \brief Validator for Channel. + * + * A specific communication connection between Asterisk and an Endpoint. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_fn(void); + +/*! + * \brief Validator for Dialed. + * + * Dialed channel information. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_dialed(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_dialed(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_dialed_fn(void); + +/*! + * \brief Validator for DialplanCEP. + * + * Dialplan location (context/extension/priority) + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_dialplan_cep(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_dialplan_cep(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_dialplan_cep_fn(void); + +/*! + * \brief Validator for Bridge. + * + * The merging of media from one or more channels. + * + * Everyone on the bridge receives the same audio. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_bridge(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_bridge(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_bridge_fn(void); + +/*! + * \brief Validator for LiveRecording. + * + * A recording that is in progress + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_live_recording(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_live_recording(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_live_recording_fn(void); + +/*! + * \brief Validator for StoredRecording. + * + * A past recording that may be played back. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_stored_recording(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_stored_recording(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_stored_recording_fn(void); + +/*! + * \brief Validator for FormatLangPair. + * + * Identifies the format and language of a sound file + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_format_lang_pair(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_format_lang_pair(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_format_lang_pair_fn(void); + +/*! + * \brief Validator for Sound. + * + * A media file that may be played back. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_sound(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_sound(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_sound_fn(void); + +/*! + * \brief Validator for Playback. + * + * Object representing the playback of media to a channel + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_playback(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_playback(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_playback_fn(void); + +/*! + * \brief Validator for ApplicationReplaced. + * + * Notification that another WebSocket has taken over for an application. + * + * An application may only be subscribed to by a single WebSocket at a time. If multiple WebSockets attempt to subscribe to the same application, the newer WebSocket wins, and the older one receives this event. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_application_replaced(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_application_replaced(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_application_replaced_fn(void); + +/*! + * \brief Validator for BridgeCreated. + * + * Notification that a bridge has been created. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_bridge_created(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_bridge_created(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_bridge_created_fn(void); + +/*! + * \brief Validator for BridgeDestroyed. + * + * Notification that a bridge has been destroyed. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_bridge_destroyed(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_bridge_destroyed(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_bridge_destroyed_fn(void); + +/*! + * \brief Validator for BridgeMerged. + * + * Notification that one bridge has merged into another. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_bridge_merged(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_bridge_merged(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_bridge_merged_fn(void); + +/*! + * \brief Validator for ChannelCallerId. + * + * Channel changed Caller ID. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_caller_id(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_caller_id(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_caller_id_fn(void); + +/*! + * \brief Validator for ChannelCreated. + * + * Notification that a channel has been created. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_created(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_created(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_created_fn(void); + +/*! + * \brief Validator for ChannelDestroyed. + * + * Notification that a channel has been destroyed. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_destroyed(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_destroyed(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_destroyed_fn(void); + +/*! + * \brief Validator for ChannelDialplan. + * + * Channel changed location in the dialplan. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_dialplan(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_dialplan(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_dialplan_fn(void); + +/*! + * \brief Validator for ChannelDtmfReceived. + * + * DTMF received on a channel. + * + * This event is sent when the DTMF ends. There is no notification about the start of DTMF + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_dtmf_received(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_dtmf_received(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_dtmf_received_fn(void); + +/*! + * \brief Validator for ChannelEnteredBridge. + * + * Notification that a channel has entered a bridge. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_entered_bridge(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_entered_bridge(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_entered_bridge_fn(void); + +/*! + * \brief Validator for ChannelHangupRequest. + * + * A hangup was requested on the channel. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_hangup_request(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_hangup_request(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_hangup_request_fn(void); + +/*! + * \brief Validator for ChannelLeftBridge. + * + * Notification that a channel has left a bridge. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_left_bridge(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_left_bridge(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_left_bridge_fn(void); + +/*! + * \brief Validator for ChannelStateChange. + * + * Notification of a channel's state change. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_state_change(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_state_change(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_state_change_fn(void); + +/*! + * \brief Validator for ChannelUserevent. + * + * User-generated event with additional user-defined fields in the object. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_userevent(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_userevent(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_userevent_fn(void); + +/*! + * \brief Validator for ChannelVarset. + * + * Channel variable changed. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_channel_varset(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_channel_varset(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_channel_varset_fn(void); + +/*! + * \brief Validator for Event. + * + * Base type for asynchronous events from Asterisk. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_event(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_event(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_event_fn(void); + +/*! + * \brief Validator for Message. + * + * Base type for errors and events + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_message(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_message(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_message_fn(void); + +/*! + * \brief Validator for MissingParams. + * + * Error event sent when required params are missing. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_missing_params(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_missing_params(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_missing_params_fn(void); + +/*! + * \brief Validator for PlaybackFinished. + * + * Event showing the completion of a media playback operation. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_playback_finished(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_playback_finished(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_playback_finished_fn(void); + +/*! + * \brief Validator for PlaybackStarted. + * + * Event showing the start of a media playback operation. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_playback_started(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_playback_started(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_playback_started_fn(void); + +/*! + * \brief Validator for StasisEnd. + * + * Notification that a channel has left a Stasis appliction. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_stasis_end(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_stasis_end(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_stasis_end_fn(void); + +/*! + * \brief Validator for StasisStart. + * + * Notification that a channel has entered a Stasis appliction. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ast_ari_validate_stasis_start(struct ast_json *json); + +/*! + * \brief Function pointer to ast_ari_validate_stasis_start(). + * + * See \ref ast_ari_model_validators.h for more details. + */ +ari_validator ast_ari_validate_stasis_start_fn(void); + +/* + * JSON models + * + * AsteriskInfo + * Variable + * - value: string (required) + * Endpoint + * - channel_ids: List[string] (required) + * - resource: string (required) + * - state: string + * - technology: string (required) + * CallerID + * - name: string (required) + * - number: string (required) + * Channel + * - accountcode: string (required) + * - caller: CallerID (required) + * - connected: CallerID (required) + * - creationtime: Date (required) + * - dialplan: DialplanCEP (required) + * - id: string (required) + * - name: string (required) + * - state: string (required) + * Dialed + * DialplanCEP + * - context: string (required) + * - exten: string (required) + * - priority: long (required) + * Bridge + * - bridge_class: string (required) + * - bridge_type: string (required) + * - channels: List[string] (required) + * - id: string (required) + * - technology: string (required) + * LiveRecording + * - format: string (required) + * - name: string (required) + * - state: string (required) + * StoredRecording + * - duration_seconds: int + * - formats: List[string] (required) + * - id: string (required) + * - time: Date + * FormatLangPair + * - format: string (required) + * - language: string (required) + * Sound + * - formats: List[FormatLangPair] (required) + * - id: string (required) + * - text: string + * Playback + * - id: string (required) + * - language: string + * - media_uri: string (required) + * - state: string (required) + * - target_uri: string (required) + * ApplicationReplaced + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * BridgeCreated + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - bridge: Bridge (required) + * BridgeDestroyed + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - bridge: Bridge (required) + * BridgeMerged + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - bridge: Bridge (required) + * - bridge_from: Bridge (required) + * ChannelCallerId + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - caller_presentation: int (required) + * - caller_presentation_txt: string (required) + * - channel: Channel (required) + * ChannelCreated + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - channel: Channel (required) + * ChannelDestroyed + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - cause: int (required) + * - cause_txt: string (required) + * - channel: Channel (required) + * ChannelDialplan + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - channel: Channel (required) + * - dialplan_app: string (required) + * - dialplan_app_data: string (required) + * ChannelDtmfReceived + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - channel: Channel (required) + * - digit: string (required) + * - duration_ms: int (required) + * ChannelEnteredBridge + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - bridge: Bridge (required) + * - channel: Channel + * ChannelHangupRequest + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - cause: int + * - channel: Channel (required) + * - soft: boolean + * ChannelLeftBridge + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - bridge: Bridge (required) + * - channel: Channel (required) + * ChannelStateChange + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - channel: Channel (required) + * ChannelUserevent + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - channel: Channel (required) + * - eventname: string (required) + * ChannelVarset + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - channel: Channel + * - value: string (required) + * - variable: string (required) + * Event + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * Message + * - type: string (required) + * MissingParams + * - type: string (required) + * - params: List[string] (required) + * PlaybackFinished + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - playback: Playback (required) + * PlaybackStarted + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - playback: Playback (required) + * StasisEnd + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - channel: Channel (required) + * StasisStart + * - type: string (required) + * - application: string (required) + * - timestamp: Date + * - args: List[string] (required) + * - channel: Channel (required) + */ + +#endif /* _ASTERISK_ARI_MODEL_H */ diff --git a/res/ari/ari_websockets.c b/res/ari/ari_websockets.c new file mode 100644 index 000000000..13650c293 --- /dev/null +++ b/res/ari/ari_websockets.c @@ -0,0 +1,165 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 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. + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/astobj2.h" +#include "asterisk/ari.h" + +/*! \file + * + * \brief WebSocket support for RESTful API's. + * \author David M. Lee, II <dlee@digium.com> + */ + +struct ast_ari_websocket_session { + struct ast_websocket *ws_session; + int (*validator)(struct ast_json *); +}; + +static void websocket_session_dtor(void *obj) +{ + struct ast_ari_websocket_session *session = obj; + + ast_websocket_unref(session->ws_session); + session->ws_session = NULL; +} + +/*! + * \brief Validator that always succeeds. + */ +static int null_validator(struct ast_json *json) +{ + return 1; +} + +struct ast_ari_websocket_session *ast_ari_websocket_session_create( + struct ast_websocket *ws_session, int (*validator)(struct ast_json *)) +{ + RAII_VAR(struct ast_ari_websocket_session *, session, NULL, ao2_cleanup); + + if (ws_session == NULL) { + return NULL; + } + + if (validator == NULL) { + validator = null_validator; + } + + if (ast_websocket_set_nonblock(ws_session) != 0) { + ast_log(LOG_ERROR, + "ARI web socket failed to set nonblock; closing: %s\n", + strerror(errno)); + return NULL; + } + + session = ao2_alloc(sizeof(*session), websocket_session_dtor); + if (!session) { + return NULL; + } + + ao2_ref(ws_session, +1); + session->ws_session = ws_session; + session->validator = validator; + + ao2_ref(session, +1); + return session; +} + +struct ast_json *ast_ari_websocket_session_read( + struct ast_ari_websocket_session *session) +{ + RAII_VAR(struct ast_json *, message, NULL, ast_json_unref); + + while (!message) { + int res; + char *payload; + uint64_t payload_len; + enum ast_websocket_opcode opcode; + int fragmented; + + res = ast_wait_for_input( + ast_websocket_fd(session->ws_session), -1); + + if (res <= 0) { + ast_log(LOG_WARNING, "WebSocket poll error: %s\n", + strerror(errno)); + return NULL; + } + + res = ast_websocket_read(session->ws_session, &payload, + &payload_len, &opcode, &fragmented); + + if (res != 0) { + ast_log(LOG_WARNING, "WebSocket read error: %s\n", + strerror(errno)); + return NULL; + } + + switch (opcode) { + case AST_WEBSOCKET_OPCODE_CLOSE: + ast_debug(1, "WebSocket closed by peer\n"); + return NULL; + case AST_WEBSOCKET_OPCODE_TEXT: + message = ast_json_load_buf(payload, payload_len, NULL); + if (message == NULL) { + ast_log(LOG_WARNING, + "WebSocket input failed to parse\n"); + } + break; + default: + /* Ignore all other message types */ + break; + } + } + + return ast_json_ref(message); +} + +#define VALIDATION_FAILED \ + "{" \ + " \"error\": \"InvalidMessage\"," \ + " \"message\": \"Message validation failed\"" \ + "}" + +int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session, + struct ast_json *message) +{ + RAII_VAR(char *, str, NULL, ast_free); + +#ifdef AST_DEVMODE + if (!session->validator(message)) { + ast_log(LOG_ERROR, "Outgoing message failed validation\n"); + return ast_websocket_write(session->ws_session, + AST_WEBSOCKET_OPCODE_TEXT, VALIDATION_FAILED, + strlen(VALIDATION_FAILED)); + } +#endif + + str = ast_json_dump_string_format(message, ast_ari_json_format()); + + if (str == NULL) { + ast_log(LOG_ERROR, "Failed to encode JSON object\n"); + return -1; + } + + return ast_websocket_write(session->ws_session, + AST_WEBSOCKET_OPCODE_TEXT, str, strlen(str)); +} diff --git a/res/ari/cli.c b/res/ari/cli.c new file mode 100644 index 000000000..819407997 --- /dev/null +++ b/res/ari/cli.c @@ -0,0 +1,267 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 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 Command line for ARI. + * \author David M. Lee, II <dlee@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/astobj2.h" +#include "asterisk/cli.h" +#include "internal.h" + +static char *ari_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); + + switch (cmd) { + case CLI_INIT: + e->command = "ari show status"; + e->usage = + "Usage: ari show status\n" + " Shows all ARI settings\n"; + return NULL; + case CLI_GENERATE: + return NULL; + default: + break; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + conf = ast_ari_config_get(); + + if (!conf) { + ast_cli(a->fd, "Error getting ARI configuration\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, "ARI Status:\n"); + ast_cli(a->fd, "Enabled: %s\n", AST_CLI_YESNO(conf->general->enabled)); + ast_cli(a->fd, "Output format: "); + switch (conf->general->format) { + case AST_JSON_COMPACT: + ast_cli(a->fd, "compact"); + break; + case AST_JSON_PRETTY: + ast_cli(a->fd, "pretty"); + break; + } + ast_cli(a->fd, "\n"); + ast_cli(a->fd, "Auth realm: %s\n", conf->general->auth_realm); + ast_cli(a->fd, "Allowed Origins: %s\n", conf->general->allowed_origins); + ast_cli(a->fd, "User count: %d\n", ao2_container_count(conf->users)); + return CLI_SUCCESS; +} + +static int show_users_cb(void *obj, void *arg, int flags) +{ + struct ast_ari_conf_user *user = obj; + struct ast_cli_args *a = arg; + + ast_cli(a->fd, "%-4s %s\n", + AST_CLI_YESNO(user->read_only), + user->username); + return 0; +} + +static char *ari_show_users(struct ast_cli_entry *e, int cmd, + struct ast_cli_args *a) +{ + RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); + + switch (cmd) { + case CLI_INIT: + e->command = "ari show users"; + e->usage = + "Usage: ari show users\n" + " Shows all ARI users\n"; + return NULL; + case CLI_GENERATE: + return NULL; + default: + break; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + conf = ast_ari_config_get(); + if (!conf) { + ast_cli(a->fd, "Error getting ARI configuration\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, "r/o? Username\n"); + ast_cli(a->fd, "---- --------\n"); + + ao2_callback(conf->users, OBJ_NODATA, show_users_cb, a); + + return CLI_SUCCESS; +} + +struct user_complete { + /*! Nth user to search for */ + int state; + /*! Which user currently on */ + int which; +}; + +static int complete_ari_user_search(void *obj, void *arg, void *data, int flags) +{ + struct user_complete *search = data; + + if (++search->which > search->state) { + return CMP_MATCH; + } + return 0; +} + +static char *complete_ari_user(struct ast_cli_args *a) +{ + RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); + RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup); + + struct user_complete search = { + .state = a->n, + }; + + conf = ast_ari_config_get(); + if (!conf) { + ast_cli(a->fd, "Error getting ARI configuration\n"); + return CLI_FAILURE; + } + + user = ao2_callback_data(conf->users, + ast_strlen_zero(a->word) ? 0 : OBJ_PARTIAL_KEY, + complete_ari_user_search, (char*)a->word, &search); + + return user ? ast_strdup(user->username) : NULL; +} + +static char *complete_ari_show_user(struct ast_cli_args *a) +{ + if (a->pos == 3) { + return complete_ari_user(a); + } + + return NULL; +} + +static char *ari_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); + RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup); + + switch (cmd) { + case CLI_INIT: + e->command = "ari show user"; + e->usage = + "Usage: ari show user <username>\n" + " Shows a specific ARI user\n"; + return NULL; + case CLI_GENERATE: + return complete_ari_show_user(a); + default: + break; + } + + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } + + conf = ast_ari_config_get(); + + if (!conf) { + ast_cli(a->fd, "Error getting ARI configuration\n"); + return CLI_FAILURE; + } + + user = ao2_find(conf->users, a->argv[3], OBJ_KEY); + if (!user) { + ast_cli(a->fd, "User '%s' not found\n", a->argv[3]); + return CLI_SUCCESS; + } + + ast_cli(a->fd, "Username: %s\n", user->username); + ast_cli(a->fd, "Read only?: %s\n", AST_CLI_YESNO(user->read_only)); + + return CLI_SUCCESS; +} + +static char *ari_mkpasswd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + RAII_VAR(char *, crypted, NULL, ast_free); + + switch (cmd) { + case CLI_INIT: + e->command = "ari mkpasswd"; + e->usage = + "Usage: ari mkpasswd <password>\n" + " Encrypts a password for use in ari.conf\n" + " Be aware that the password will be shown in the\n" + " command line history. The mkpasswd shell command\n" + " may be preferable.\n" + ; + return NULL; + case CLI_GENERATE: + return NULL; + default: + break; + } + + if (a->argc != 3) { + return CLI_SHOWUSAGE; + } + + crypted = ast_crypt_encrypt(a->argv[2]); + if (!crypted) { + ast_cli(a->fd, "Failed to encrypt password\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, + "; Copy the following two lines into ari.conf\n"); + ast_cli(a->fd, "password_format = crypt\n"); + ast_cli(a->fd, "password = %s\n", crypted); + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_ari[] = { + AST_CLI_DEFINE(ari_show, "Show ARI settings"), + AST_CLI_DEFINE(ari_show_users, "List ARI users"), + AST_CLI_DEFINE(ari_show_user, "List single ARI user"), + AST_CLI_DEFINE(ari_mkpasswd, "Encrypts a password"), +}; + +int ast_ari_cli_register(void) { + return ast_cli_register_multiple(cli_ari, ARRAY_LEN(cli_ari)); +} + +void ast_ari_cli_unregister(void) { + ast_cli_unregister_multiple(cli_ari, ARRAY_LEN(cli_ari)); +} diff --git a/res/ari/config.c b/res/ari/config.c new file mode 100644 index 000000000..e25fa8ad1 --- /dev/null +++ b/res/ari/config.c @@ -0,0 +1,345 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 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 Config framework stuffz for ARI. + * \author David M. Lee, II <dlee@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/config_options.h" +#include "internal.h" + +/*! \brief Locking container for safe configuration access. */ +static AO2_GLOBAL_OBJ_STATIC(confs); + +/*! \brief Mapping of the ARI conf struct's globals to the + * general context in the config file. */ +static struct aco_type general_option = { + .type = ACO_GLOBAL, + .name = "general", + .item_offset = offsetof(struct ast_ari_conf, general), + .category = "^general$", + .category_match = ACO_WHITELIST, +}; + +static struct aco_type *general_options[] = ACO_TYPES(&general_option); + +/*! \brief Encoding format handler converts from boolean to enum. */ +static int encoding_format_handler(const struct aco_option *opt, + struct ast_variable *var, void *obj) +{ + struct ast_ari_conf_general *general = obj; + + if (!strcasecmp(var->name, "pretty")) { + general->format = ast_true(var->value) ? + AST_JSON_PRETTY : AST_JSON_COMPACT; + } else { + return -1; + } + + return 0; +} + +/*! \brief Parses the ast_ari_password_format enum from a config file */ +static int password_format_handler(const struct aco_option *opt, + struct ast_variable *var, void *obj) +{ + struct ast_ari_conf_user *user = obj; + + if (strcasecmp(var->value, "plain") == 0) { + user->password_format = ARI_PASSWORD_FORMAT_PLAIN; + } else if (strcasecmp(var->value, "crypt") == 0) { + user->password_format = ARI_PASSWORD_FORMAT_CRYPT; + } else { + return -1; + } + + return 0; +} + +/*! \brief Destructor for \ref ast_ari_conf_user */ +static void user_dtor(void *obj) +{ + struct ast_ari_conf_user *user = obj; + ast_debug(3, "Disposing of user %s\n", user->username); + ast_free(user->username); +} + +/*! \brief Allocate an \ref ast_ari_conf_user for config parsing */ +static void *user_alloc(const char *cat) +{ + RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup); + + if (!cat) { + return NULL; + } + + ast_debug(3, "Allocating user %s\n", cat); + + user = ao2_alloc_options(sizeof(*user), user_dtor, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!user) { + return NULL; + } + + user->username = ast_strdup(cat); + if (!user->username) { + return NULL; + } + + ao2_ref(user, +1); + return user; +} + +/*! \brief Sorting function for use with red/black tree */ +static int user_sort_cmp(const void *obj_left, const void *obj_right, int flags) +{ + const struct ast_ari_conf_user *user_left = obj_left; + + if (flags & OBJ_PARTIAL_KEY) { + const char *key_right = obj_right; + return strncasecmp(user_left->username, key_right, + strlen(key_right)); + } else if (flags & OBJ_KEY) { + const char *key_right = obj_right; + return strcasecmp(user_left->username, key_right); + } else { + const struct ast_ari_conf_user *user_right = obj_right; + const char *key_right = user_right->username; + return strcasecmp(user_left->username, key_right); + } +} + +/*! \brief \ref aco_type item_find function */ +static void *user_find(struct ao2_container *tmp_container, const char *cat) +{ + if (!cat) { + return NULL; + } + + return ao2_find(tmp_container, cat, OBJ_KEY); +} + +static struct aco_type user_option = { + .type = ACO_ITEM, + .name = "user", + .category_match = ACO_BLACKLIST, + .category = "^general$", + .matchfield = "type", + .matchvalue = "user", + .item_alloc = user_alloc, + .item_find = user_find, + .item_offset = offsetof(struct ast_ari_conf, users), +}; + +static struct aco_type *user[] = ACO_TYPES(&user_option); + +/*! \brief \ref ast_ari_conf destructor. */ +static void conf_destructor(void *obj) +{ + struct ast_ari_conf *cfg = obj; + + ast_string_field_free_memory(cfg->general); + + ao2_cleanup(cfg->general); + ao2_cleanup(cfg->users); +} + +/*! \brief Allocate an \ref ast_ari_conf for config parsing */ +static void *conf_alloc(void) +{ + RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup); + + cfg = ao2_alloc_options(sizeof(*cfg), conf_destructor, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!cfg) { + return NULL; + } + + cfg->general = ao2_alloc_options(sizeof(*cfg->general), NULL, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!cfg->general) { + return NULL; + } + aco_set_defaults(&general_option, "general", cfg->general); + + if (ast_string_field_init(cfg->general, 64)) { + return NULL; + } + + cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, + AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL); + + ao2_ref(cfg, +1); + return cfg; +} + +#define CONF_FILENAME "ari.conf" + +/*! \brief The conf file that's processed for the module. */ +static struct aco_file conf_file = { + /*! The config file name. */ + .filename = CONF_FILENAME, + /*! The mapping object types to be processed. */ + .types = ACO_TYPES(&general_option, &user_option), +}; + +CONFIG_INFO_STANDARD(cfg_info, confs, conf_alloc, + .files = ACO_FILES(&conf_file)); + +struct ast_ari_conf *ast_ari_config_get(void) +{ + struct ast_ari_conf *res = ao2_global_obj_ref(confs); + if (!res) { + ast_log(LOG_ERROR, + "Error obtaining config from " CONF_FILENAME "\n"); + } + return res; +} + +struct ast_ari_conf_user *ast_ari_config_validate_user(const char *username, + const char *password) +{ + RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); + RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup); + int is_valid = 0; + + conf = ast_ari_config_get(); + if (!conf) { + return NULL; + } + + user = ao2_find(conf->users, username, OBJ_KEY); + if (!user) { + return NULL; + } + + if (ast_strlen_zero(user->password)) { + ast_log(LOG_WARNING, + "User '%s' missing password; authentication failed\n", + user->username); + return NULL; + } + + switch (user->password_format) { + case ARI_PASSWORD_FORMAT_PLAIN: + is_valid = strcmp(password, user->password) == 0; + break; + case ARI_PASSWORD_FORMAT_CRYPT: + is_valid = ast_crypt_validate(password, user->password); + break; + } + + if (!is_valid) { + return NULL; + } + + ao2_ref(user, +1); + return user; +} + +/*! \brief Callback to validate a user object */ +static int validate_user_cb(void *obj, void *arg, int flags) +{ + struct ast_ari_conf_user *user = obj; + + if (ast_strlen_zero(user->password)) { + ast_log(LOG_WARNING, "User '%s' missing password\n", + user->username); + } + + return 0; +} + +/*! \brief Load (or reload) configuration. */ +static int process_config(int reload) +{ + RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); + + switch (aco_process_config(&cfg_info, reload)) { + case ACO_PROCESS_ERROR: + return -1; + case ACO_PROCESS_OK: + case ACO_PROCESS_UNCHANGED: + break; + } + + conf = ast_ari_config_get(); + if (!conf) { + ast_assert(0); /* We just configured; it should be there */ + return -1; + } + + if (ao2_container_count(conf->users) == 0) { + ast_log(LOG_ERROR, "No configured users for ARI\n"); + } + + ao2_callback(conf->users, OBJ_NODATA, validate_user_cb, NULL); + + return 0; +} + +int ast_ari_config_init(void) +{ + if (aco_info_init(&cfg_info)) { + aco_info_destroy(&cfg_info); + return -1; + } + + aco_option_register(&cfg_info, "enabled", ACO_EXACT, general_options, + "yes", OPT_BOOL_T, 1, + FLDSET(struct ast_ari_conf_general, enabled)); + aco_option_register_custom(&cfg_info, "pretty", ACO_EXACT, + general_options, "no", encoding_format_handler, 0); + aco_option_register(&cfg_info, "auth_realm", ACO_EXACT, general_options, + "Asterisk REST Interface", OPT_CHAR_ARRAY_T, 0, + FLDSET(struct ast_ari_conf_general, auth_realm), + ARI_AUTH_REALM_LEN); + aco_option_register(&cfg_info, "allowed_origins", ACO_EXACT, general_options, + "", OPT_STRINGFIELD_T, 0, + STRFLDSET(struct ast_ari_conf_general, allowed_origins)); + + aco_option_register(&cfg_info, "type", ACO_EXACT, user, NULL, + OPT_NOOP_T, 0, 0); + aco_option_register(&cfg_info, "read_only", ACO_EXACT, user, + "no", OPT_BOOL_T, 1, + FLDSET(struct ast_ari_conf_user, read_only)); + aco_option_register(&cfg_info, "password", ACO_EXACT, user, + "", OPT_CHAR_ARRAY_T, 0, + FLDSET(struct ast_ari_conf_user, password), ARI_PASSWORD_LEN); + aco_option_register_custom(&cfg_info, "password_format", ACO_EXACT, + user, "plain", password_format_handler, 0); + + return process_config(0); +} + +int ast_ari_config_reload(void) +{ + return process_config(1); +} + +void ast_ari_config_destroy(void) +{ + aco_info_destroy(&cfg_info); + ao2_global_obj_release(confs); +} diff --git a/res/ari/internal.h b/res/ari/internal.h new file mode 100644 index 000000000..ffacd4939 --- /dev/null +++ b/res/ari/internal.h @@ -0,0 +1,143 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 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. + */ + +#ifndef ARI_INTERNAL_H_ +#define ARI_INTERNAL_H_ + +/*! \file + * + * \brief Internal API's for res_ari. + * \author David M. Lee, II <dlee@digium.com> + */ + +#include "asterisk/json.h" + +/*! @{ */ + +/*! + * \brief Register CLI commands for ARI. + * + * \return 0 on success. + * \return Non-zero on error. + */ +int ast_ari_cli_register(void); + +/*! + * \brief Unregister CLI commands for ARI. + */ +void ast_ari_cli_unregister(void); + +/*! @} */ + +/*! @{ */ + +struct ast_ari_conf_general; + +/*! \brief All configuration options for ARI. */ +struct ast_ari_conf { + /*! The general section configuration options. */ + struct ast_ari_conf_general *general; + /*! Configured users */ + struct ao2_container *users; +}; + +/*! Max length for auth_realm field */ +#define ARI_AUTH_REALM_LEN 80 + +/*! \brief Global configuration options for ARI. */ +struct ast_ari_conf_general { + /*! Enabled by default, disabled if false. */ + int enabled; + /*! Encoding format used during output (default compact). */ + enum ast_json_encoding_format format; + /*! Authentication realm */ + char auth_realm[ARI_AUTH_REALM_LEN]; + + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(allowed_origins); + ); +}; + +/*! \brief Password format */ +enum ast_ari_password_format { + /*! \brief Plaintext password */ + ARI_PASSWORD_FORMAT_PLAIN, + /*! crypt(3) password */ + ARI_PASSWORD_FORMAT_CRYPT, +}; + +/*! + * \brief User's password mx length. + * + * If 256 seems like a lot, a crypt SHA-512 has over 106 characters. + */ +#define ARI_PASSWORD_LEN 256 + +/*! \brief Per-user configuration options */ +struct ast_ari_conf_user { + /*! Username for authentication */ + char *username; + /*! User's password. */ + char password[ARI_PASSWORD_LEN]; + /*! Format for the password field */ + enum ast_ari_password_format password_format; + /*! If true, user cannot execute change operations */ + int read_only; +}; + +/*! + * \brief Initialize the ARI configuration + */ +int ast_ari_config_init(void); + +/*! + * \brief Reload the ARI configuration + */ +int ast_ari_config_reload(void); + +/*! + * \brief Destroy the ARI configuration + */ +void ast_ari_config_destroy(void); + +/*! + * \brief Get the current ARI configuration. + * + * This is an immutable object, so don't modify it. It is AO2 managed, so + * ao2_cleanup() when you're done with it. + * + * \return ARI configuration object. + * \return \c NULL on error. + */ +struct ast_ari_conf *ast_ari_config_get(void); + +/*! + * \brief Validated a user's credentials. + * + * \param username Name of the user. + * \param password User's password. + * \return User object. + * \return \c NULL if username or password is invalid. + */ +struct ast_ari_conf_user *ast_ari_config_validate_user(const char *username, + const char *password); + +/*! @} */ + + +#endif /* ARI_INTERNAL_H_ */ diff --git a/res/ari/resource_asterisk.c b/res/ari/resource_asterisk.c new file mode 100644 index 000000000..dac45714f --- /dev/null +++ b/res/ari/resource_asterisk.c @@ -0,0 +1,80 @@ +/* -*- 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 Implementation for ARI stubs. + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "resource_asterisk.h" +#include "asterisk/pbx.h" + +void ast_ari_get_asterisk_info(struct ast_variable *headers, struct ast_get_asterisk_info_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ari_get_asterisk_info\n"); +} + +void ast_ari_get_global_var(struct ast_variable *headers, struct ast_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, ast_str_create(32), ast_free); + + const char *value; + + ast_assert(response != NULL); + + 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)); +} + +void ast_ari_set_global_var(struct ast_variable *headers, struct ast_set_global_var_args *args, struct ast_ari_response *response) +{ + ast_assert(response != NULL); + + if (ast_strlen_zero(args->variable)) { + ast_ari_response_error( + response, 400, "Bad Request", + "Variable name is required"); + return; + } + + pbx_builtin_setvar_helper(NULL, args->variable, args->value); + + ast_ari_response_no_content(response); +} diff --git a/res/ari/resource_asterisk.h b/res/ari/resource_asterisk.h new file mode 100644 index 000000000..69539525e --- /dev/null +++ b/res/ari/resource_asterisk.h @@ -0,0 +1,84 @@ +/* + * 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 Generated file - declares stubs to be implemented in + * res/ari/resource_asterisk.c + * + * Asterisk resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_ASTERISK_H +#define _ASTERISK_RESOURCE_ASTERISK_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_get_asterisk_info() */ +struct ast_get_asterisk_info_args { + /*! \brief Filter information returned */ + const char *only; +}; +/*! + * \brief Gets Asterisk system information. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_asterisk_info(struct ast_variable *headers, struct ast_get_asterisk_info_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_global_var() */ +struct ast_get_global_var_args { + /*! \brief The variable to get */ + const char *variable; +}; +/*! + * \brief Get the value of a global variable. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_global_var(struct ast_variable *headers, struct ast_get_global_var_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_set_global_var() */ +struct ast_set_global_var_args { + /*! \brief The variable to set */ + const char *variable; + /*! \brief The value to set the variable to */ + const char *value; +}; +/*! + * \brief Set the value of a global variable. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_set_global_var(struct ast_variable *headers, struct ast_set_global_var_args *args, struct ast_ari_response *response); + +#endif /* _ASTERISK_RESOURCE_ASTERISK_H */ diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c new file mode 100644 index 000000000..17a8bb132 --- /dev/null +++ b/res/ari/resource_bridges.c @@ -0,0 +1,514 @@ +/* -*- 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 Implementation for ARI stubs. + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "resource_bridges.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_bridges.h" +#include "asterisk/stasis_app.h" +#include "asterisk/stasis_app_playback.h" +#include "asterisk/stasis_app_recording.h" +#include "asterisk/stasis_channels.h" +#include "asterisk/core_unreal.h" +#include "asterisk/channel.h" +#include "asterisk/bridge.h" +#include "asterisk/format_cap.h" +#include "asterisk/file.h" + +/*! + * \brief Finds a bridge, filling the response with an error, if appropriate. + * + * \param[out] response Response to fill with an error if control is not found. + * \param bridge_id ID of the bridge to lookup. + * + * \return Bridget. + * \return \c NULL if bridge does not exist. + */ +static struct ast_bridge *find_bridge( + struct ast_ari_response *response, + const char *bridge_id) +{ + RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup); + + ast_assert(response != NULL); + + bridge = stasis_app_bridge_find_by_id(bridge_id); + if (bridge == NULL) { + RAII_VAR(struct ast_bridge_snapshot *, snapshot, + ast_bridge_snapshot_get_latest(bridge_id), ao2_cleanup); + if (!snapshot) { + ast_ari_response_error(response, 404, "Not found", + "Bridge not found"); + return NULL; + } + + ast_ari_response_error(response, 409, "Conflict", + "Bridge not in Stasis application"); + return NULL; + } + + ao2_ref(bridge, +1); + return bridge; +} + +/*! + * \brief Finds the control object for a channel, filling the response with an + * error, if appropriate. + * \param[out] response Response to fill with an error if control is not found. + * \param channel_id ID of the channel to lookup. + * \return Channel control object. + * \return \c NULL if control object does not exist. + */ +static struct stasis_app_control *find_channel_control( + struct ast_ari_response *response, + const char *channel_id) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + + ast_assert(response != NULL); + + control = stasis_app_control_find_by_channel_id(channel_id); + if (control == NULL) { + ast_ari_response_error(response, 422, "Unprocessable Entity", + "Channel not in Stasis application"); + return NULL; + } + + ao2_ref(control, +1); + return control; +} + +void ast_ari_add_channel_to_bridge(struct ast_variable *headers, struct ast_add_channel_to_bridge_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + if (!bridge) { + return; + } + + control = find_channel_control(response, args->channel); + if (!control) { + return; + } + + stasis_app_control_add_channel_to_bridge(control, bridge); + ast_ari_response_no_content(response); +} + +void ast_ari_remove_channel_from_bridge(struct ast_variable *headers, struct ast_remove_channel_from_bridge_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + if (!bridge) { + return; + } + + control = find_channel_control(response, args->channel); + if (!control) { + return; + } + + /* BUGBUG this should make sure the bridge requested for removal is actually + * the bridge the channel is in. This will be possible once the bridge uniqueid + * is added to the channel snapshot. A 409 response should be issued if the bridge + * uniqueids don't match */ + if (stasis_app_control_remove_channel_from_bridge(control, bridge)) { + ast_ari_response_error(response, 500, "Internal Error", + "Could not remove channel from bridge"); + return; + } + + ast_ari_response_no_content(response); +} + +struct bridge_channel_control_thread_data { + struct ast_channel *bridge_channel; + struct stasis_app_control *control; +}; + +static void *bridge_channel_control_thread(void *data) +{ + struct bridge_channel_control_thread_data *thread_data = data; + struct ast_channel *bridge_channel = thread_data->bridge_channel; + struct stasis_app_control *control = thread_data->control; + + RAII_VAR(struct ast_callid *, callid, ast_channel_callid(bridge_channel), ast_callid_cleanup); + + if (callid) { + ast_callid_threadassoc_add(callid); + } + + ast_free(thread_data); + thread_data = NULL; + + stasis_app_control_execute_until_exhausted(bridge_channel, control); + + ast_hangup(bridge_channel); + ao2_cleanup(control); + return NULL; +} + +static struct ast_channel *prepare_bridge_media_channel(const char *type) +{ + RAII_VAR(struct ast_format_cap *, cap, NULL, ast_format_cap_destroy); + struct ast_format format; + + cap = ast_format_cap_alloc_nolock(); + if (!cap) { + return NULL; + } + + ast_format_cap_add(cap, ast_format_set(&format, AST_FORMAT_SLINEAR, 0)); + + if (!cap) { + return NULL; + } + + return ast_request(type, cap, NULL, "ARI", NULL); +} + +void ast_ari_play_on_bridge(struct ast_variable *headers, struct ast_play_on_bridge_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 *, play_channel, NULL, ast_hangup); + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); + RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup); + RAII_VAR(char *, playback_url, NULL, ast_free); + RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); + + struct bridge_channel_control_thread_data *thread_data; + const char *language; + pthread_t threadid; + + ast_assert(response != NULL); + + if (!bridge) { + return; + } + + 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)); + + if (ast_unreal_channel_push_to_bridge(play_channel, bridge)) { + 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; + } + + snapshot = stasis_app_control_get_snapshot(control); + if (!snapshot) { + ast_ari_response_error( + response, 500, "Internal Error", "Failed to get control snapshot"); + return; + } + + language = S_OR(args->lang, snapshot->language); + + playback = stasis_app_control_play_uri(control, args->media, language, + args->bridge_id, STASIS_PLAYBACK_TARGET_BRIDGE, args->skipms, + args->offsetms); + + if (!playback) { + ast_ari_response_alloc_failed(response); + return; + } + + ast_asprintf(&playback_url, "/playback/%s", + stasis_app_playback_get_id(playback)); + + if (!playback_url) { + ast_ari_response_alloc_failed(response); + return; + } + + json = stasis_app_playback_to_json(playback); + if (!json) { + 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; + + 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; + + ast_ari_response_created(response, playback_url, json); +} + +void ast_ari_record_bridge(struct ast_variable *headers, struct ast_record_bridge_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); + + 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; + } + + if (ast_unreal_channel_push_to_bridge(record_channel, bridge)) { + 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; + } + + 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; + + 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 in progress", + 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); + + ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name); + if (!recording_url) { + 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; + + 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; + + ast_ari_response_created(response, recording_url, json); +} + +void ast_ari_get_bridge(struct ast_variable *headers, struct ast_get_bridge_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct ast_bridge_snapshot *, snapshot, ast_bridge_snapshot_get_latest(args->bridge_id), ao2_cleanup); + if (!snapshot) { + ast_ari_response_error( + response, 404, "Not Found", + "Bridge not found"); + return; + } + + ast_ari_response_ok(response, + ast_bridge_snapshot_to_json(snapshot)); +} + +void ast_ari_delete_bridge(struct ast_variable *headers, struct ast_delete_bridge_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); + if (!bridge) { + return; + } + + stasis_app_bridge_destroy(args->bridge_id); + ast_ari_response_no_content(response); +} + +void ast_ari_get_bridges(struct ast_variable *headers, struct ast_get_bridges_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_caching_topic *, caching_topic, 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; + + caching_topic = ast_bridge_topic_all_cached(); + if (!caching_topic) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Message bus not initialized"); + return; + } + ao2_ref(caching_topic, +1); + + snapshots = stasis_cache_dump(caching_topic, 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); + if (ast_json_array_append(json, ast_bridge_snapshot_to_json(snapshot))) { + ast_ari_response_alloc_failed(response); + return; + } + } + ao2_iterator_destroy(&i); + + ast_ari_response_ok(response, ast_json_ref(json)); +} + +void ast_ari_new_bridge(struct ast_variable *headers, struct ast_new_bridge_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct ast_bridge *, bridge, stasis_app_bridge_create(args->type), ao2_cleanup); + RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup); + + if (!bridge) { + ast_ari_response_error( + response, 500, "Internal Error", + "Unable to create bridge"); + return; + } + + snapshot = ast_bridge_snapshot_create(bridge); + if (!snapshot) { + ast_ari_response_error( + response, 500, "Internal Error", + "Unable to create snapshot for new bridge"); + return; + } + + ast_ari_response_ok(response, + ast_bridge_snapshot_to_json(snapshot)); +} diff --git a/res/ari/resource_bridges.h b/res/ari/resource_bridges.h new file mode 100644 index 000000000..892a3f269 --- /dev/null +++ b/res/ari/resource_bridges.h @@ -0,0 +1,179 @@ +/* + * 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 Generated file - declares stubs to be implemented in + * res/ari/resource_bridges.c + * + * Bridge resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_BRIDGES_H +#define _ASTERISK_RESOURCE_BRIDGES_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_get_bridges() */ +struct ast_get_bridges_args { +}; +/*! + * \brief List active bridges. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_bridges(struct ast_variable *headers, struct ast_get_bridges_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_new_bridge() */ +struct ast_new_bridge_args { + /*! \brief Type of bridge to create. */ + const char *type; +}; +/*! + * \brief Create a new bridge. + * + * This bridge persists until it has been shut down, or Asterisk has been shut down. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_new_bridge(struct ast_variable *headers, struct ast_new_bridge_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_bridge() */ +struct ast_get_bridge_args { + /*! \brief Bridge's id */ + const char *bridge_id; +}; +/*! + * \brief Get bridge details. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_bridge(struct ast_variable *headers, struct ast_get_bridge_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_delete_bridge() */ +struct ast_delete_bridge_args { + /*! \brief Bridge's id */ + const char *bridge_id; +}; +/*! + * \brief Shut down a bridge. + * + * If any channels are in this bridge, they will be removed and resume whatever they were doing beforehand. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_delete_bridge(struct ast_variable *headers, struct ast_delete_bridge_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_add_channel_to_bridge() */ +struct ast_add_channel_to_bridge_args { + /*! \brief Bridge's id */ + const char *bridge_id; + /*! \brief Channel's id */ + const char *channel; +}; +/*! + * \brief Add a channel to a bridge. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_add_channel_to_bridge(struct ast_variable *headers, struct ast_add_channel_to_bridge_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_remove_channel_from_bridge() */ +struct ast_remove_channel_from_bridge_args { + /*! \brief Bridge's id */ + const char *bridge_id; + /*! \brief Channel's id */ + const char *channel; +}; +/*! + * \brief Remove a channel from a bridge. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_remove_channel_from_bridge(struct ast_variable *headers, struct ast_remove_channel_from_bridge_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_play_on_bridge() */ +struct ast_play_on_bridge_args { + /*! \brief Bridge's id */ + const char *bridge_id; + /*! \brief Media's URI to play. */ + const char *media; + /*! \brief For sounds, selects language for sound. */ + const char *lang; + /*! \brief Number of media to skip before playing. */ + int offsetms; + /*! \brief Number of milliseconds to skip for forward/reverse operations. */ + int skipms; +}; +/*! + * \brief Start playback of media on a bridge. + * + * The media URI may be any of a number of URI's. You may use http: and https: URI's, as well as sound: and recording: URI's. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.) + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_play_on_bridge(struct ast_variable *headers, struct ast_play_on_bridge_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_record_bridge() */ +struct ast_record_bridge_args { + /*! \brief Bridge's id */ + const char *bridge_id; + /*! \brief Recording's filename */ + const char *name; + /*! \brief Format to encode audio in */ + const char *format; + /*! \brief Maximum duration of the recording, in seconds. 0 for no limit. */ + int max_duration_seconds; + /*! \brief Maximum duration of silence, in seconds. 0 for no limit. */ + int max_silence_seconds; + /*! \brief Action to take if a recording with the same name already exists. */ + const char *if_exists; + /*! \brief Play beep when recording begins */ + int beep; + /*! \brief DTMF input to terminate recording. */ + const char *terminate_on; +}; +/*! + * \brief Start a recording. + * + * This records the mixed audio from all channels participating in this bridge. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_record_bridge(struct ast_variable *headers, struct ast_record_bridge_args *args, struct ast_ari_response *response); + +#endif /* _ASTERISK_RESOURCE_BRIDGES_H */ diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c new file mode 100644 index 000000000..7f3a91fba --- /dev/null +++ b/res/ari/resource_channels.c @@ -0,0 +1,693 @@ +/* + * 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 Implementation for ARI stubs. + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/*** MODULEINFO + <depend type="module">res_stasis_app_playback</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/file.h" +#include "asterisk/pbx.h" +#include "asterisk/dial.h" +#include "asterisk/bridge.h" +#include "asterisk/callerid.h" +#include "asterisk/stasis_app.h" +#include "asterisk/stasis_app_playback.h" +#include "asterisk/stasis_app_recording.h" +#include "asterisk/stasis_channels.h" +#include "resource_channels.h" + +#include <limits.h> + +/*! + * \brief Finds the control object for a channel, filling the response with an + * error, if appropriate. + * \param[out] response Response to fill with an error if control is not found. + * \param channel_id ID of the channel to lookup. + * \return Channel control object. + * \return \c NULL if control object does not exist. + */ +static struct stasis_app_control *find_control( + struct ast_ari_response *response, + const char *channel_id) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + + ast_assert(response != NULL); + + control = stasis_app_control_find_by_channel_id(channel_id); + if (control == NULL) { + /* Distinguish between 404 and 409 errors */ + RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup); + chan = ast_channel_get_by_name(channel_id); + if (chan == NULL) { + ast_ari_response_error(response, 404, "Not Found", + "Channel not found"); + return NULL; + } + + ast_ari_response_error(response, 409, "Conflict", + "Channel not in Stasis application"); + return NULL; + } + + ao2_ref(control, +1); + return control; +} + +void ast_ari_dial(struct ast_variable *headers, struct ast_dial_args *args, struct ast_ari_response *response) +{ + struct stasis_app_control *control; + + control = find_control(response, args->channel_id); + if (control == NULL) { + return; + } + + if (stasis_app_control_dial(control, args->endpoint, args->timeout)) { + ast_ari_response_alloc_failed(response); + return; + } + + ast_ari_response_no_content(response); +} + +void ast_ari_continue_in_dialplan( + struct ast_variable *headers, + struct ast_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); +} + +void ast_ari_answer_channel(struct ast_variable *headers, + struct ast_answer_channel_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + + control = find_control(response, args->channel_id); + if (control == NULL) { + return; + } + + if (stasis_app_control_answer(control) != 0) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Failed to answer channel"); + return; + } + + ast_ari_response_no_content(response); +} + +void ast_ari_mute_channel(struct ast_variable *headers, struct ast_mute_channel_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + unsigned int direction = 0; + enum ast_frame_type frametype = AST_FRAME_VOICE; + + control = find_control(response, args->channel_id); + if (control == NULL) { + return; + } + + if (!strcmp(args->direction, "in")) { + direction = AST_MUTE_DIRECTION_READ; + } else if (!strcmp(args->direction, "out")) { + direction = AST_MUTE_DIRECTION_WRITE; + } else if (!strcmp(args->direction, "both")) { + direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE; + } else { + ast_ari_response_error( + response, 400, "Bad Request", + "Invalid direction specified"); + return; + } + + stasis_app_control_mute(control, direction, frametype); + + ast_ari_response_no_content(response); +} + +void ast_ari_unmute_channel(struct ast_variable *headers, struct ast_unmute_channel_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + unsigned int direction = 0; + enum ast_frame_type frametype = AST_FRAME_VOICE; + + control = find_control(response, args->channel_id); + if (control == NULL) { + return; + } + + if (!strcmp(args->direction, "in")) { + direction = AST_MUTE_DIRECTION_READ; + } else if (!strcmp(args->direction, "out")) { + direction = AST_MUTE_DIRECTION_WRITE; + } else if (!strcmp(args->direction, "both")) { + direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE; + } else { + ast_ari_response_error( + response, 400, "Bad Request", + "Invalid direction specified"); + return; + } + + stasis_app_control_unmute(control, direction, frametype); + + ast_ari_response_no_content(response); +} + +void ast_ari_hold_channel(struct ast_variable *headers, struct ast_hold_channel_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + + control = find_control(response, args->channel_id); + if (control == NULL) { + /* Response filled in by find_control */ + return; + } + + stasis_app_control_hold(control); + + ast_ari_response_no_content(response); +} + +void ast_ari_unhold_channel(struct ast_variable *headers, struct ast_unhold_channel_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + + control = find_control(response, args->channel_id); + if (control == NULL) { + /* Response filled in by find_control */ + return; + } + + stasis_app_control_unhold(control); + + ast_ari_response_no_content(response); +} + +void ast_ari_moh_start_channel(struct ast_variable *headers, struct ast_moh_start_channel_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + + control = find_control(response, args->channel_id); + if (control == NULL) { + /* Response filled in by find_control */ + return; + } + + stasis_app_control_moh_start(control, args->moh_class); + ast_ari_response_no_content(response); +} + +void ast_ari_moh_stop_channel(struct ast_variable *headers, struct ast_moh_stop_channel_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + + control = find_control(response, args->channel_id); + if (control == NULL) { + /* Response filled in by find_control */ + return; + } + + stasis_app_control_moh_stop(control); + ast_ari_response_no_content(response); +} + +void ast_ari_play_on_channel(struct ast_variable *headers, + struct ast_play_on_channel_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); + RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup); + RAII_VAR(char *, playback_url, NULL, ast_free); + RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); + const char *language; + + ast_assert(response != NULL); + + control = find_control(response, args->channel_id); + if (control == NULL) { + /* Response filled in by find_control */ + return; + } + + snapshot = stasis_app_control_get_snapshot(control); + if (!snapshot) { + ast_ari_response_error( + response, 404, "Not Found", + "Channel not found"); + return; + } + + if (args->skipms < 0) { + ast_ari_response_error( + response, 400, "Bad Request", + "skipms cannot be negative"); + return; + } + + if (args->offsetms < 0) { + ast_ari_response_error( + response, 400, "Bad Request", + "offsetms cannot be negative"); + return; + } + + language = S_OR(args->lang, snapshot->language); + + playback = stasis_app_control_play_uri(control, args->media, language, + args->channel_id, STASIS_PLAYBACK_TARGET_CHANNEL, args->skipms, args->offsetms); + if (!playback) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Failed to queue media for playback"); + return; + } + + ast_asprintf(&playback_url, "/playback/%s", + stasis_app_playback_get_id(playback)); + if (!playback_url) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + return; + } + + json = stasis_app_playback_to_json(playback); + if (!json) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + return; + } + + ast_ari_response_created(response, playback_url, json); +} + +void ast_ari_record_channel(struct ast_variable *headers, + struct ast_record_channel_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); + 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); + size_t uri_name_maxlen; + + ast_assert(response != NULL); + + if (args->max_duration_seconds < 0) { + ast_ari_response_error( + response, 400, "Bad Request", + "max_duration_seconds cannot be negative"); + return; + } + + if (args->max_silence_seconds < 0) { + ast_ari_response_error( + response, 400, "Bad Request", + "max_silence_seconds cannot be negative"); + return; + } + + control = find_control(response, args->channel_id); + if (control == NULL) { + /* Response filled in by find_control */ + return; + } + + options = stasis_app_recording_options_create(args->name, args->format); + if (options == NULL) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + } + options->max_silence_seconds = args->max_silence_seconds; + options->max_duration_seconds = args->max_duration_seconds; + options->terminate_on = + stasis_app_recording_termination_parse(args->terminate_on); + options->if_exists = + stasis_app_recording_if_exists_parse(args->if_exists); + options->beep = args->beep; + + if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) { + ast_ari_response_error( + response, 400, "Bad Request", + "terminateOn invalid"); + return; + } + + if (options->if_exists == -1) { + ast_ari_response_error( + response, 400, "Bad Request", + "ifExists invalid"); + return; + } + + 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 in progress", + args->name); + break; + case ENOMEM: + ast_ari_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + break; + case EPERM: + ast_ari_response_error( + response, 400, "Bad Request", + "Recording name invalid"); + break; + default: + ast_log(LOG_WARNING, + "Unrecognized recording error: %s\n", + strerror(errno)); + ast_ari_response_error( + response, 500, "Internal Server Error", + "Internal Server Error"); + break; + } + return; + } + + uri_name_maxlen = strlen(args->name) * 3; + uri_encoded_name = ast_malloc(uri_name_maxlen); + if (!uri_encoded_name) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + return; + } + ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen, + ast_uri_http); + + ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name); + if (!recording_url) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + return; + } + + json = stasis_app_recording_to_json(recording); + if (!json) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Out of memory"); + return; + } + + ast_ari_response_created(response, recording_url, json); +} + +void ast_ari_get_channel(struct ast_variable *headers, + struct ast_get_channel_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + struct stasis_caching_topic *caching_topic; + struct ast_channel_snapshot *snapshot; + + caching_topic = ast_channel_topic_all_cached(); + if (!caching_topic) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Message bus not initialized"); + return; + } + + msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(), + args->channel_id); + if (!msg) { + ast_ari_response_error( + response, 404, "Not Found", + "Channel not found"); + return; + } + + snapshot = stasis_message_data(msg); + ast_assert(snapshot != NULL); + + ast_ari_response_ok(response, + ast_channel_snapshot_to_json(snapshot)); +} + +void ast_ari_delete_channel(struct ast_variable *headers, + struct ast_delete_channel_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup); + + chan = ast_channel_get_by_name(args->channel_id); + if (chan == NULL) { + ast_ari_response_error( + response, 404, "Not Found", + "Channel not found"); + return; + } + + ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT); + + ast_ari_response_no_content(response); +} + +void ast_ari_get_channels(struct ast_variable *headers, + struct ast_get_channels_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_caching_topic *, caching_topic, 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; + + caching_topic = ast_channel_topic_all_cached(); + if (!caching_topic) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Message bus not initialized"); + return; + } + ao2_ref(caching_topic, +1); + + snapshots = stasis_cache_dump(caching_topic, 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; + } + + i = ao2_iterator_init(snapshots, 0); + while ((obj = ao2_iterator_next(&i))) { + RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup); + struct ast_channel_snapshot *snapshot = stasis_message_data(msg); + int r = ast_json_array_append( + json, ast_channel_snapshot_to_json(snapshot)); + if (r != 0) { + ast_ari_response_alloc_failed(response); + return; + } + } + ao2_iterator_destroy(&i); + + ast_ari_response_ok(response, ast_json_ref(json)); +} + +void ast_ari_originate(struct ast_variable *headers, + struct ast_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; + + 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, NULL)) { + ast_ari_response_alloc_failed(response); + return; + } + } else if (!ast_strlen_zero(args->extension)) { + /* originate a channel, sending it to an extension */ + if (ast_pbx_outgoing_exten(dialtech, NULL, dialdevice, timeout, S_OR(args->context, "default"), args->extension, args->priority ? args->priority : 1, NULL, 0, cid_num, cid_name, NULL, NULL, NULL, 0)) { + ast_ari_response_alloc_failed(response); + return; + } + } else { + ast_ari_response_error(response, 400, "Bad Request", + "Application or extension must be specified"); + return; + } + + ast_ari_response_no_content(response); +} + +void ast_ari_get_channel_var(struct ast_variable *headers, struct ast_get_channel_var_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); + RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); + RAII_VAR(char *, value, NULL, ast_free); + + ast_assert(response != NULL); + + control = find_control(response, args->channel_id); + if (control == NULL) { + return; + } + + value = stasis_app_control_get_channel_var(control, 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)); +} + +void ast_ari_set_channel_var(struct ast_variable *headers, struct ast_set_channel_var_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 (ast_strlen_zero(args->variable)) { + ast_ari_response_error( + response, 400, "Bad Request", + "Variable name is required"); + return; + } + + if (stasis_app_control_set_channel_var(control, args->variable, args->value)) { + ast_ari_response_error( + response, 400, "Bad Request", + "Failed to execute function"); + return; + } + + ast_ari_response_no_content(response); +} + diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h new file mode 100644 index 000000000..953d58f4e --- /dev/null +++ b/res/ari/resource_channels.h @@ -0,0 +1,330 @@ +/* + * 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 Generated file - declares stubs to be implemented in + * res/ari/resource_channels.c + * + * Channel resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_CHANNELS_H +#define _ASTERISK_RESOURCE_CHANNELS_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_get_channels() */ +struct ast_get_channels_args { +}; +/*! + * \brief List active channels. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_channels(struct ast_variable *headers, struct ast_get_channels_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_originate() */ +struct ast_originate_args { + /*! \brief Endpoint to call. */ + const char *endpoint; + /*! \brief The extension to dial after the endpoint answers */ + const char *extension; + /*! \brief The context to dial after the endpoint answers. If omitted, uses 'default' */ + const char *context; + /*! \brief The priority to dial after the endpoint answers. If omitted, uses 1 */ + long priority; + /*! \brief The application name to pass to the Stasis application. */ + const char *app; + /*! \brief The application arguments to pass to the Stasis application. */ + const char *app_args; + /*! \brief CallerID to use when dialing the endpoint or extension. */ + const char *caller_id; + /*! \brief Timeout (in seconds) before giving up dialing, or -1 for no timeout. */ + int timeout; +}; +/*! + * \brief Create a new channel (originate). + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_originate(struct ast_variable *headers, struct ast_originate_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_channel() */ +struct ast_get_channel_args { + /*! \brief Channel's id */ + const char *channel_id; +}; +/*! + * \brief Channel details. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_channel(struct ast_variable *headers, struct ast_get_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_delete_channel() */ +struct ast_delete_channel_args { + /*! \brief Channel's id */ + const char *channel_id; +}; +/*! + * \brief Delete (i.e. hangup) a channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_delete_channel(struct ast_variable *headers, struct ast_delete_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_dial() */ +struct ast_dial_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief Endpoint to call. If not specified, dial is routed via dialplan */ + const char *endpoint; + /*! \brief Extension to dial */ + const char *extension; + /*! \brief When routing via dialplan, the context use. If omitted, uses 'default' */ + const char *context; + /*! \brief Timeout (in seconds) before giving up dialing, or -1 for no timeout. */ + int timeout; +}; +/*! + * \brief Create a new channel (originate) and bridge to this channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_dial(struct ast_variable *headers, struct ast_dial_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_continue_in_dialplan() */ +struct ast_continue_in_dialplan_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief The context to continue to. */ + const char *context; + /*! \brief The extension to continue to. */ + const char *extension; + /*! \brief The priority to continue to. */ + int priority; +}; +/*! + * \brief Exit application; continue execution in the dialplan. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_continue_in_dialplan(struct ast_variable *headers, struct ast_continue_in_dialplan_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_answer_channel() */ +struct ast_answer_channel_args { + /*! \brief Channel's id */ + const char *channel_id; +}; +/*! + * \brief Answer a channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_answer_channel(struct ast_variable *headers, struct ast_answer_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_mute_channel() */ +struct ast_mute_channel_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief Direction in which to mute audio */ + const char *direction; +}; +/*! + * \brief Mute a channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_mute_channel(struct ast_variable *headers, struct ast_mute_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_unmute_channel() */ +struct ast_unmute_channel_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief Direction in which to unmute audio */ + const char *direction; +}; +/*! + * \brief Unmute a channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_unmute_channel(struct ast_variable *headers, struct ast_unmute_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_hold_channel() */ +struct ast_hold_channel_args { + /*! \brief Channel's id */ + const char *channel_id; +}; +/*! + * \brief Hold a channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_hold_channel(struct ast_variable *headers, struct ast_hold_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_unhold_channel() */ +struct ast_unhold_channel_args { + /*! \brief Channel's id */ + const char *channel_id; +}; +/*! + * \brief Remove a channel from hold. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_unhold_channel(struct ast_variable *headers, struct ast_unhold_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_moh_start_channel() */ +struct ast_moh_start_channel_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief Music on hold class to use */ + const char *moh_class; +}; +/*! + * \brief Play music on hold to a channel. + * + * Using media operations such as playOnChannel on a channel playing MOH in this manner will suspend MOH without resuming automatically. If continuing music on hold is desired, the stasis application must reinitiate music on hold. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_moh_start_channel(struct ast_variable *headers, struct ast_moh_start_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_moh_stop_channel() */ +struct ast_moh_stop_channel_args { + /*! \brief Channel's id */ + const char *channel_id; +}; +/*! + * \brief Stop playing music on hold to a channel. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_moh_stop_channel(struct ast_variable *headers, struct ast_moh_stop_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_play_on_channel() */ +struct ast_play_on_channel_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief Media's URI to play. */ + const char *media; + /*! \brief For sounds, selects language for sound. */ + const char *lang; + /*! \brief Number of media to skip before playing. */ + int offsetms; + /*! \brief Number of milliseconds to skip for forward/reverse operations. */ + int skipms; +}; +/*! + * \brief Start playback of media. + * + * The media URI may be any of a number of URI's. You may use http: and https: URI's, as well as sound: and recording: URI's. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.) + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_play_on_channel(struct ast_variable *headers, struct ast_play_on_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_record_channel() */ +struct ast_record_channel_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief Recording's filename */ + const char *name; + /*! \brief Format to encode audio in */ + const char *format; + /*! \brief Maximum duration of the recording, in seconds. 0 for no limit */ + int max_duration_seconds; + /*! \brief Maximum duration of silence, in seconds. 0 for no limit */ + int max_silence_seconds; + /*! \brief Action to take if a recording with the same name already exists. */ + const char *if_exists; + /*! \brief Play beep when recording begins */ + int beep; + /*! \brief DTMF input to terminate recording */ + const char *terminate_on; +}; +/*! + * \brief Start a recording. + * + * Record audio from a channel. Note that this will not capture audio sent to the channel. The bridge itself has a record feature if that's what you want. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_record_channel(struct ast_variable *headers, struct ast_record_channel_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_channel_var() */ +struct ast_get_channel_var_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief The channel variable or function to get */ + const char *variable; +}; +/*! + * \brief Get the value of a channel variable or function. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_channel_var(struct ast_variable *headers, struct ast_get_channel_var_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_set_channel_var() */ +struct ast_set_channel_var_args { + /*! \brief Channel's id */ + const char *channel_id; + /*! \brief The channel variable or function to set */ + const char *variable; + /*! \brief The value to set the variable to */ + const char *value; +}; +/*! + * \brief Set the value of a channel variable or function. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_set_channel_var(struct ast_variable *headers, struct ast_set_channel_var_args *args, struct ast_ari_response *response); + +#endif /* _ASTERISK_RESOURCE_CHANNELS_H */ diff --git a/res/ari/resource_endpoints.c b/res/ari/resource_endpoints.c new file mode 100644 index 000000000..bb28df03c --- /dev/null +++ b/res/ari/resource_endpoints.c @@ -0,0 +1,157 @@ +/* + * 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" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "resource_endpoints.h" + +#include "asterisk/astobj2.h" +#include "asterisk/stasis.h" +#include "asterisk/stasis_endpoints.h" + +void ast_ari_get_endpoints(struct ast_variable *headers, + struct ast_get_endpoints_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_caching_topic *, caching_topic, 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; + + caching_topic = ast_endpoint_topic_all_cached(); + if (!caching_topic) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Message bus not initialized"); + return; + } + ao2_ref(caching_topic, +1); + + snapshots = stasis_cache_dump(caching_topic, 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); + int r = ast_json_array_append( + json, ast_endpoint_snapshot_to_json(snapshot)); + if (r != 0) { + ast_ari_response_alloc_failed(response); + return; + } + } + ao2_iterator_destroy(&i); + + ast_ari_response_ok(response, ast_json_ref(json)); +} +void ast_ari_get_endpoints_by_tech(struct ast_variable *headers, + struct ast_get_endpoints_by_tech_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_caching_topic *, caching_topic, 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; + + /* TODO - if tech isn't a recognized type of endpoint, it should 404 */ + + caching_topic = ast_endpoint_topic_all_cached(); + if (!caching_topic) { + ast_ari_response_error( + response, 500, "Internal Server Error", + "Message bus not initialized"); + return; + } + ao2_ref(caching_topic, +1); + + snapshots = stasis_cache_dump(caching_topic, 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); + int r; + + if (strcmp(args->tech, snapshot->tech) != 0) { + continue; + } + + r = ast_json_array_append( + json, ast_endpoint_snapshot_to_json(snapshot)); + if (r != 0) { + ast_ari_response_alloc_failed(response); + return; + } + } + ao2_iterator_destroy(&i); + + ast_ari_response_ok(response, ast_json_ref(json)); +} +void ast_ari_get_endpoint(struct ast_variable *headers, + struct ast_get_endpoint_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, 0); + if (!snapshot) { + ast_ari_response_error(response, 404, "Not Found", + "Endpoint not found"); + return; + } + + json = ast_endpoint_snapshot_to_json(snapshot); + if (!json) { + ast_ari_response_alloc_failed(response); + return; + } + + ast_ari_response_ok(response, ast_json_ref(json)); +} diff --git a/res/ari/resource_endpoints.h b/res/ari/resource_endpoints.h new file mode 100644 index 000000000..82c6afc0a --- /dev/null +++ b/res/ari/resource_endpoints.h @@ -0,0 +1,82 @@ +/* + * 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 Generated file - declares stubs to be implemented in + * res/ari/resource_endpoints.c + * + * Endpoint resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_ENDPOINTS_H +#define _ASTERISK_RESOURCE_ENDPOINTS_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_get_endpoints() */ +struct ast_get_endpoints_args { +}; +/*! + * \brief List all endoints. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_endpoints(struct ast_variable *headers, struct ast_get_endpoints_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_endpoints_by_tech() */ +struct ast_get_endpoints_by_tech_args { + /*! \brief Technology of the endpoints (sip,iax2,...) */ + const char *tech; +}; +/*! + * \brief List available endoints for a given endpoint technology. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_endpoints_by_tech(struct ast_variable *headers, struct ast_get_endpoints_by_tech_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_endpoint() */ +struct ast_get_endpoint_args { + /*! \brief Technology of the endpoint */ + const char *tech; + /*! \brief ID of the endpoint */ + const char *resource; +}; +/*! + * \brief Details for an endpoint. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_endpoint(struct ast_variable *headers, struct ast_get_endpoint_args *args, struct ast_ari_response *response); + +#endif /* _ASTERISK_RESOURCE_ENDPOINTS_H */ diff --git a/res/ari/resource_events.c b/res/ari/resource_events.c new file mode 100644 index 000000000..e5490b546 --- /dev/null +++ b/res/ari/resource_events.c @@ -0,0 +1,218 @@ +/* + * 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/events.{format} implementation- WebSocket resource + * + * \author David M. Lee, II <dlee@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/astobj2.h" +#include "asterisk/stasis_app.h" +#include "resource_events.h" + +/*! Number of buckets for the Stasis application hash table. Remember to keep it + * a prime number! + */ +#define APPS_NUM_BUCKETS 7 + +/*! \brief A connection to the event WebSocket */ +struct event_session { + struct ast_ari_websocket_session *ws_session; + struct ao2_container *websocket_apps; +}; + +/*! + * \brief Explicitly shutdown a session. + * + * An explicit shutdown is necessary, since stasis-app has a reference to this + * session. We also need to be sure to null out the \c ws_session field, since + * the websocket is about to go away. + * + * \param session Session info struct. + */ +static void session_shutdown(struct event_session *session) +{ + struct ao2_iterator i; + char *app; + SCOPED_AO2LOCK(lock, session); + + i = ao2_iterator_init(session->websocket_apps, 0); + while ((app = ao2_iterator_next(&i))) { + stasis_app_unregister(app); + ao2_cleanup(app); + } + ao2_iterator_destroy(&i); + ao2_cleanup(session->websocket_apps); + + session->websocket_apps = NULL; + session->ws_session = NULL; +} + +static void session_dtor(void *obj) +{ +#ifdef AST_DEVMODE /* Avoid unused variable warning */ + struct event_session *session = obj; +#endif + + /* session_shutdown should have been called before */ + ast_assert(session->ws_session == NULL); + ast_assert(session->websocket_apps == NULL); +} + +static void session_cleanup(struct event_session *session) +{ + session_shutdown(session); + ao2_cleanup(session); +} + +static struct event_session *session_create( + struct ast_ari_websocket_session *ws_session) +{ + RAII_VAR(struct event_session *, session, NULL, ao2_cleanup); + + session = ao2_alloc(sizeof(*session), session_dtor); + + session->ws_session = ws_session; + session->websocket_apps = + ast_str_container_alloc(APPS_NUM_BUCKETS); + + if (!session->websocket_apps) { + return NULL; + } + + ao2_ref(session, +1); + return session; +} + +/*! + * \brief Callback handler for Stasis application messages. + */ +static void app_handler(void *data, const char *app_name, + struct ast_json *message) +{ + struct event_session *session = data; + int res; + const char *msg_type = S_OR( + ast_json_string_get(ast_json_object_get(message, "type")), + ""); + const char *msg_application = S_OR( + ast_json_string_get(ast_json_object_get(message, "application")), + ""); + + /* Determine if we've been replaced */ + if (strcmp(msg_type, "ApplicationReplaced") == 0 && + strcmp(msg_application, app_name) == 0) { + ao2_find(session->websocket_apps, msg_application, + OBJ_UNLINK | OBJ_NODATA); + } + + res = ast_json_object_set(message, "application", + ast_json_string_create(app_name)); + if(res != 0) { + return; + } + + ao2_lock(session); + if (session->ws_session) { + ast_ari_websocket_session_write(session->ws_session, message); + } + ao2_unlock(session); +} + +/*! + * \brief Register for all of the apps given. + * \param session Session info struct. + * \param app_list Comma seperated list of app names to register. + */ +static int session_register_apps(struct event_session *session, + const char *app_list) +{ + RAII_VAR(char *, to_free, NULL, ast_free); + char *apps, *app_name; + SCOPED_AO2LOCK(lock, session); + + ast_assert(session->ws_session != NULL); + ast_assert(session->websocket_apps != NULL); + + if (!app_list) { + return -1; + } + + to_free = apps = ast_strdup(app_list); + if (!apps) { + ast_ari_websocket_session_write(session->ws_session, ast_ari_oom_json()); + return -1; + } + while ((app_name = strsep(&apps, ","))) { + if (ast_str_container_add(session->websocket_apps, app_name)) { + ast_ari_websocket_session_write(session->ws_session, ast_ari_oom_json()); + return -1; + } + + stasis_app_register(app_name, app_handler, session); + } + return 0; +} + +void ast_ari_websocket_event_websocket(struct ast_ari_websocket_session *ws_session, + struct ast_variable *headers, + struct ast_event_websocket_args *args) +{ + RAII_VAR(struct event_session *, session, NULL, session_cleanup); + struct ast_json *msg; + int res; + + ast_debug(3, "/events WebSocket connection\n"); + + session = session_create(ws_session); + if (!session) { + ast_ari_websocket_session_write(ws_session, ast_ari_oom_json()); + return; + } + + if (!args->app) { + RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref); + + msg = ast_json_pack("{s: s, s: [s]}", + "type", "MissingParams", + "params", "app"); + if (!msg) { + msg = ast_json_ref(ast_ari_oom_json()); + } + + ast_ari_websocket_session_write(session->ws_session, msg); + return; + } + + res = session_register_apps(session, args->app); + if (res != 0) { + ast_ari_websocket_session_write(ws_session, ast_ari_oom_json()); + return; + } + + /* We don't process any input, but we'll consume it waiting for EOF */ + while ((msg = ast_ari_websocket_session_read(ws_session))) { + ast_json_unref(msg); + } +} diff --git a/res/ari/resource_events.h b/res/ari/resource_events.h new file mode 100644 index 000000000..554ed9a87 --- /dev/null +++ b/res/ari/resource_events.h @@ -0,0 +1,56 @@ +/* + * 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 Generated file - declares stubs to be implemented in + * res/ari/resource_events.c + * + * WebSocket resource + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_EVENTS_H +#define _ASTERISK_RESOURCE_EVENTS_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_event_websocket() */ +struct ast_event_websocket_args { + /*! \brief Comma seperated list of applications to subscribe to. */ + const char *app; +}; +/*! + * \brief WebSocket connection for events. + * + * \param session ARI WebSocket. + * \param headers HTTP headers. + * \param args Swagger parameters. + */ +void ast_ari_websocket_event_websocket(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_event_websocket_args *args); + +#endif /* _ASTERISK_RESOURCE_EVENTS_H */ diff --git a/res/ari/resource_playback.c b/res/ari/resource_playback.c new file mode 100644 index 000000000..64afe10c1 --- /dev/null +++ b/res/ari/resource_playback.c @@ -0,0 +1,137 @@ +/* + * 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/playback.{format} implementation- Playback control resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/stasis_app_playback.h" +#include "resource_playback.h" + +void ast_ari_get_playback(struct ast_variable *headers, + struct ast_get_playback_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); + + playback = stasis_app_playback_find_by_id(args->playback_id); + if (playback == NULL) { + ast_ari_response_error(response, 404, "Not Found", + "Playback not found"); + return; + } + + json = stasis_app_playback_to_json(playback); + if (json == NULL) { + ast_ari_response_error(response, 500, + "Internal Server Error", "Error building response"); + return; + } + + ast_ari_response_ok(response, ast_json_ref(json)); +} +void ast_ari_stop_playback(struct ast_variable *headers, + struct ast_stop_playback_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup); + enum stasis_playback_oper_results res; + + playback = stasis_app_playback_find_by_id(args->playback_id); + if (playback == NULL) { + ast_ari_response_error(response, 404, "Not Found", + "Playback not found"); + return; + } + + res = stasis_app_playback_operation(playback, STASIS_PLAYBACK_STOP); + + switch (res) { + case STASIS_PLAYBACK_OPER_OK: + ast_ari_response_no_content(response); + return; + case STASIS_PLAYBACK_OPER_FAILED: + ast_ari_response_error(response, 500, + "Internal Server Error", "Could not stop playback"); + return; + case STASIS_PLAYBACK_OPER_NOT_PLAYING: + /* Stop operation should be valid even when not playing */ + ast_assert(0); + ast_ari_response_error(response, 500, + "Internal Server Error", "Could not stop playback"); + return; + } +} +void ast_ari_control_playback(struct ast_variable *headers, + struct ast_control_playback_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup); + enum stasis_app_playback_media_operation oper; + enum stasis_playback_oper_results res; + + if (strcmp(args->operation, "unpause") == 0) { + oper = STASIS_PLAYBACK_UNPAUSE; + } else if (strcmp(args->operation, "pause") == 0) { + oper = STASIS_PLAYBACK_PAUSE; + } else if (strcmp(args->operation, "restart") == 0) { + oper = STASIS_PLAYBACK_RESTART; + } else if (strcmp(args->operation, "reverse") == 0) { + oper = STASIS_PLAYBACK_REVERSE; + } else if (strcmp(args->operation, "forward") == 0) { + oper = STASIS_PLAYBACK_FORWARD; + } else { + ast_ari_response_error(response, 400, + "Bad Request", "Invalid operation %s", + args->operation); + return; + + } + + playback = stasis_app_playback_find_by_id(args->playback_id); + if (playback == NULL) { + ast_ari_response_error(response, 404, "Not Found", + "Playback not found"); + return; + } + + res = stasis_app_playback_operation(playback, oper); + + switch (res) { + case STASIS_PLAYBACK_OPER_OK: + ast_ari_response_no_content(response); + return; + case STASIS_PLAYBACK_OPER_FAILED: + ast_ari_response_error(response, 500, + "Internal Server Error", "Could not %s playback", + args->operation); + return; + case STASIS_PLAYBACK_OPER_NOT_PLAYING: + ast_ari_response_error(response, 409, "Conflict", + "Can only %s while media is playing", args->operation); + return; + } +} diff --git a/res/ari/resource_playback.h b/res/ari/resource_playback.h new file mode 100644 index 000000000..c606a5caf --- /dev/null +++ b/res/ari/resource_playback.h @@ -0,0 +1,84 @@ +/* + * 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 Generated file - declares stubs to be implemented in + * res/ari/resource_playback.c + * + * Playback control resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_PLAYBACK_H +#define _ASTERISK_RESOURCE_PLAYBACK_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_get_playback() */ +struct ast_get_playback_args { + /*! \brief Playback's id */ + const char *playback_id; +}; +/*! + * \brief Get a playback's details. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_playback(struct ast_variable *headers, struct ast_get_playback_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_stop_playback() */ +struct ast_stop_playback_args { + /*! \brief Playback's id */ + const char *playback_id; +}; +/*! + * \brief Stop a playback. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_stop_playback(struct ast_variable *headers, struct ast_stop_playback_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_control_playback() */ +struct ast_control_playback_args { + /*! \brief Playback's id */ + const char *playback_id; + /*! \brief Operation to perform on the playback. */ + const char *operation; +}; +/*! + * \brief Get a playback's details. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_control_playback(struct ast_variable *headers, struct ast_control_playback_args *args, struct ast_ari_response *response); + +#endif /* _ASTERISK_RESOURCE_PLAYBACK_H */ diff --git a/res/ari/resource_recordings.c b/res/ari/resource_recordings.c new file mode 100644 index 000000000..46439ff0b --- /dev/null +++ b/res/ari/resource_recordings.c @@ -0,0 +1,97 @@ +/* + * 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/recordings.{format} implementation- Recording resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/stasis_app_recording.h" +#include "resource_recordings.h" + +void ast_ari_get_stored_recordings(struct ast_variable *headers, struct ast_get_stored_recordings_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_get_stored_recordings\n"); +} +void ast_ari_get_stored_recording(struct ast_variable *headers, struct ast_get_stored_recording_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_get_stored_recording\n"); +} +void ast_ari_delete_stored_recording(struct ast_variable *headers, struct ast_delete_stored_recording_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_delete_stored_recording\n"); +} +void ast_ari_get_live_recordings(struct ast_variable *headers, struct ast_get_live_recordings_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_get_live_recordings\n"); +} + +void ast_ari_get_live_recording(struct ast_variable *headers, + struct ast_get_live_recording_args *args, + struct ast_ari_response *response) +{ + RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup); + RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); + + recording = stasis_app_recording_find_by_name(args->recording_name); + if (recording == NULL) { + ast_ari_response_error(response, 404, "Not Found", + "Recording not found"); + return; + } + + json = stasis_app_recording_to_json(recording); + if (json == NULL) { + ast_ari_response_error(response, 500, + "Internal Server Error", "Error building response"); + return; + } + + ast_ari_response_ok(response, ast_json_ref(json)); +} + +void ast_ari_cancel_recording(struct ast_variable *headers, struct ast_cancel_recording_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_cancel_recording\n"); +} +void ast_ari_stop_recording(struct ast_variable *headers, struct ast_stop_recording_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_stop_recording\n"); +} +void ast_ari_pause_recording(struct ast_variable *headers, struct ast_pause_recording_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_pause_recording\n"); +} +void ast_ari_unpause_recording(struct ast_variable *headers, struct ast_unpause_recording_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_unpause_recording\n"); +} +void ast_ari_mute_recording(struct ast_variable *headers, struct ast_mute_recording_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_mute_recording\n"); +} +void ast_ari_unmute_recording(struct ast_variable *headers, struct ast_unmute_recording_args *args, struct ast_ari_response *response) +{ + ast_log(LOG_ERROR, "TODO: ast_ari_unmute_recording\n"); +} diff --git a/res/ari/resource_recordings.h b/res/ari/resource_recordings.h new file mode 100644 index 000000000..e3ee88be8 --- /dev/null +++ b/res/ari/resource_recordings.h @@ -0,0 +1,186 @@ +/* + * 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 Generated file - declares stubs to be implemented in + * res/ari/resource_recordings.c + * + * Recording resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_RECORDINGS_H +#define _ASTERISK_RESOURCE_RECORDINGS_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_get_stored_recordings() */ +struct ast_get_stored_recordings_args { +}; +/*! + * \brief List recordings that are complete. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_stored_recordings(struct ast_variable *headers, struct ast_get_stored_recordings_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_stored_recording() */ +struct ast_get_stored_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief Get a stored recording's details. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_stored_recording(struct ast_variable *headers, struct ast_get_stored_recording_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_delete_stored_recording() */ +struct ast_delete_stored_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief Delete a stored recording. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_delete_stored_recording(struct ast_variable *headers, struct ast_delete_stored_recording_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_live_recordings() */ +struct ast_get_live_recordings_args { +}; +/*! + * \brief List libe recordings. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_live_recordings(struct ast_variable *headers, struct ast_get_live_recordings_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_live_recording() */ +struct ast_get_live_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief List live recordings. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_live_recording(struct ast_variable *headers, struct ast_get_live_recording_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_cancel_recording() */ +struct ast_cancel_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief Stop a live recording and discard it. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_cancel_recording(struct ast_variable *headers, struct ast_cancel_recording_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_stop_recording() */ +struct ast_stop_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief Stop a live recording and store it. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_stop_recording(struct ast_variable *headers, struct ast_stop_recording_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_pause_recording() */ +struct ast_pause_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief Pause a live recording. + * + * Pausing a recording suspends silence detection, which will be restarted when the recording is unpaused. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_pause_recording(struct ast_variable *headers, struct ast_pause_recording_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_unpause_recording() */ +struct ast_unpause_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief Unpause a live recording. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_unpause_recording(struct ast_variable *headers, struct ast_unpause_recording_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_mute_recording() */ +struct ast_mute_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief Mute a live recording. + * + * Muting a recording suspends silence detection, which will be restarted when the recording is unmuted. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_mute_recording(struct ast_variable *headers, struct ast_mute_recording_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_unmute_recording() */ +struct ast_unmute_recording_args { + /*! \brief The name of the recording */ + const char *recording_name; +}; +/*! + * \brief Unmute a live recording. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_unmute_recording(struct ast_variable *headers, struct ast_unmute_recording_args *args, struct ast_ari_response *response); + +#endif /* _ASTERISK_RESOURCE_RECORDINGS_H */ diff --git a/res/ari/resource_sounds.c b/res/ari/resource_sounds.c new file mode 100644 index 000000000..f77c8a227 --- /dev/null +++ b/res/ari/resource_sounds.c @@ -0,0 +1,220 @@ +/* + * 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/sounds.{format} implementation- Sound resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "resource_sounds.h" +#include "asterisk/media_index.h" +#include "asterisk/sounds_index.h" +#include "asterisk/format.h" +#include "asterisk/format_cap.h" +#include "asterisk/json.h" + +/*! \brief arguments that are necessary for adding format/lang pairs */ +struct lang_format_info { + struct ast_json *format_list; /*!< The embedded array to which format/lang pairs should be added */ + const char *filename; /*!< Name of the file for which to add format/lang pairs */ + const char *format_filter; /*!< Format filter provided in the request */ +}; + +/*! \brief Add format/lang pairs to the array embedded in the sound object */ +static int add_format_information_cb(void *obj, void *arg, int flags) +{ + char *language = obj; + struct lang_format_info *args = arg; + struct ast_format format; + RAII_VAR(struct ast_format_cap *, cap, NULL, ast_format_cap_destroy); + RAII_VAR(struct ast_media_index *, sounds_index, ast_sounds_get_index(), ao2_cleanup); + + if (!sounds_index) { + return CMP_STOP; + } + + cap = ast_media_get_format_cap(sounds_index, args->filename, language); + if (!cap) { + return CMP_STOP; + } + + ast_format_cap_iter_start(cap); + while (!ast_format_cap_iter_next(cap, &format)) { + struct ast_json *lang_format_pair; + const char *format_name = ast_getformatname(&format); + + if (!ast_strlen_zero(args->format_filter) + && strcmp(args->format_filter, format_name)) { + continue; + } + + lang_format_pair = ast_json_pack("{s: s, s: s}", + "language", language, + "format", format_name); + if (!lang_format_pair) { + ast_format_cap_iter_end(cap); + return CMP_STOP; + } + + ast_json_array_append(args->format_list, lang_format_pair); + } + ast_format_cap_iter_end(cap); + return 0; +} + +/*! \brief Filter out all languages not matching the specified language */ +static int filter_langs_cb(void *obj, void *arg, int flags) +{ + char *lang_filter = arg; + char *lang = obj; + if (strcmp(lang, lang_filter)) { + return CMP_MATCH; + } + return 0; +} + +/*! \brief Generate a Sound structure as documented in sounds.json for the specified filename */ +static struct ast_json *create_sound_blob(const char *filename, struct ast_get_sounds_args *args) +{ + RAII_VAR(struct ast_json *, sound, NULL, ast_json_unref); + RAII_VAR(struct ao2_container *, languages, NULL, ao2_cleanup); + const char *description; + struct ast_json *format_lang_list; + struct lang_format_info info; + RAII_VAR(struct ast_media_index *, sounds_index, ast_sounds_get_index(), ao2_cleanup); + + if (!sounds_index) { + return NULL; + } + + description = ast_media_get_description(sounds_index, filename, "en"); + if (ast_strlen_zero(description)) { + sound = ast_json_pack("{s: s, s: []}", + "id", filename, + "formats"); + } else { + sound = ast_json_pack("{s: s, s: s, s: []}", + "id", filename, + "text", description, + "formats"); + } + if (!sound) { + return NULL; + } + + format_lang_list = ast_json_object_get(sound, "formats"); + if (!format_lang_list) { + return NULL; + } + + languages = ast_media_get_variants(sounds_index, filename); + if (!languages || !ao2_container_count(languages)) { + return NULL; + } + + /* filter requested languages */ + if (args && !ast_strlen_zero(args->lang)) { + char *lang_filter = ast_strdupa(args->lang); + ao2_callback(languages, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, filter_langs_cb, lang_filter); + if (!languages || !ao2_container_count(languages)) { + return NULL; + } + } + + info.filename = filename; + info.format_list = format_lang_list; + info.format_filter = NULL; + if (args) { + info.format_filter = args->format; + } + ao2_callback(languages, OBJ_NODATA, add_format_information_cb, &info); + + /* no format/lang pairs for this sound so nothing to return */ + if (!ast_json_array_size(format_lang_list)) { + return NULL; + } + + return ast_json_ref(sound); +} + +/*! \brief Generate a Sound structure and append it to the output blob */ +static int append_sound_cb(void *obj, void *arg, void *data, int flags) +{ + struct ast_json *sounds_array = arg; + char *filename = obj; + struct ast_get_sounds_args *args = data; + struct ast_json *sound_blob = create_sound_blob(filename, args); + if (!sound_blob) { + return 0; + } + + ast_json_array_append(sounds_array, sound_blob); + return 0; +} + +void ast_ari_get_sounds(struct ast_variable *headers, struct ast_get_sounds_args *args, struct ast_ari_response *response) +{ + RAII_VAR(struct ao2_container *, sound_files, NULL, ao2_cleanup); + struct ast_json *sounds_blob; + RAII_VAR(struct ast_media_index *, sounds_index, ast_sounds_get_index(), ao2_cleanup); + + if (!sounds_index) { + ast_ari_response_error(response, 500, "Internal Error", "Sounds index not available"); + return; + } + + sound_files = ast_media_get_media(sounds_index); + if (!sound_files) { + ast_ari_response_error(response, 500, "Internal Error", "Allocation Error"); + return; + } + + sounds_blob = ast_json_array_create(); + if (!sounds_blob) { + ast_ari_response_error(response, 500, "Internal Error", "Allocation Error"); + return; + } + + ao2_callback_data(sound_files, OBJ_NODATA, append_sound_cb, sounds_blob, args); + + if (!ast_json_array_size(sounds_blob)) { + ast_ari_response_error(response, 404, "Not Found", "No sounds found that matched the query"); + return; + } + + ast_ari_response_ok(response, sounds_blob); +} + +void ast_ari_get_stored_sound(struct ast_variable *headers, struct ast_get_stored_sound_args *args, struct ast_ari_response *response) +{ + struct ast_json *sound_blob; + + sound_blob = create_sound_blob(args->sound_id, NULL); + if (!sound_blob) { + ast_ari_response_error(response, 404, "Not Found", "Sound not found"); + return; + } + + ast_ari_response_ok(response, sound_blob); +} diff --git a/res/ari/resource_sounds.h b/res/ari/resource_sounds.h new file mode 100644 index 000000000..7cb22fb71 --- /dev/null +++ b/res/ari/resource_sounds.h @@ -0,0 +1,69 @@ +/* + * 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 Generated file - declares stubs to be implemented in + * res/ari/resource_sounds.c + * + * Sound resources + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!!!! DO NOT EDIT !!!!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file is generated by a mustache template. Please see the original + * template in rest-api-templates/ari_resource.h.mustache + */ + +#ifndef _ASTERISK_RESOURCE_SOUNDS_H +#define _ASTERISK_RESOURCE_SOUNDS_H + +#include "asterisk/ari.h" + +/*! \brief Argument struct for ast_ari_get_sounds() */ +struct ast_get_sounds_args { + const char *lang; + const char *format; +}; +/*! + * \brief List all sounds. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_sounds(struct ast_variable *headers, struct ast_get_sounds_args *args, struct ast_ari_response *response); +/*! \brief Argument struct for ast_ari_get_stored_sound() */ +struct ast_get_stored_sound_args { + /*! \brief Sound's id */ + const char *sound_id; +}; +/*! + * \brief Get a sound's details. + * + * \param headers HTTP headers + * \param args Swagger parameters + * \param[out] response HTTP response + */ +void ast_ari_get_stored_sound(struct ast_variable *headers, struct ast_get_stored_sound_args *args, struct ast_ari_response *response); + +#endif /* _ASTERISK_RESOURCE_SOUNDS_H */ |