Adds the ability to attach multiple states to both Channels and Bridges in the form
of variables that are included in all events on the associated object.
First, this adds an optional boolean field to channel variables 'report_events'
that causes the variable to automatically be included in all events on that channel.
To allow this, variables can now be either name value pairs (the current format):
`<variable_name>: '<value_string>'`
- or -
`<variable_name>: {value: '<value_string>', report_events: [true|false]}`
If the old format is used or 'report_events' is not included, it will default to
false and retain current behavior.
Second, this extends both reported and unreported variables to Bridges so they too
may have stateful information.
Resolves: #1910
UserNote: Bridge variables now can be set and retrieved via the following paths:
`/bridges/{bridgeId}/variable`
`/bridges/{bridgeId}/variables`
Both Bridge and Channel variables can now be set with an optional 'report_events'
boolean flag that will cause those variables to be included on all events on that
object. The 'report_events' flag will default to False if not set to maintain
backwards capability.
To allow this, variables can now be either name value pairs (the current format):
`<variable_name>: '<value_string>'`
- or -
`<variable_name>: {value: '<value_string>', report_events: [true|false]}`
enum ast_bridge_video_mode_type video_mode;
/*! The time of bridge creation */
struct timeval creationtime;
+ /*! Variables to be included in ARI events */
+ struct varshead *bridgevars;
};
/*!
struct ast_bridge_snapshot *current_snapshot;
/*! The time of bridge creation */
struct timeval creationtime;
+ /*! A linked list for bridge variables */
+ struct varshead bridgevars;
+ /*! A vector of variable names to be included in ARI events on this bridge */
+ AST_VECTOR(, char *) ari_reportable_variable_names;
};
/*! \brief Bridge base class virtual method table. */
*/
void ast_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid);
+/*!
+ * \brief Set a variable on the bridge.
+ * \since 20.20.0
+ * \since 22.10.0
+ * \since 23.4.0
+ *
+ * \pre Bridge is locked
+ *
+ * \param bridge Bridge to operate on.
+ * \param name Name of variable to set.
+ * \param value Value of variable to set. (NULL to delete variable.)
+ * \param report_events If non-zero, the variable change will be reported in ARI events.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_bridge_set_variable(struct ast_bridge *bridge, const char *name, const char *value, int report_events);
+
+/*!
+ * \brief Get value a variable from the bridge by name.
+ * \since 20.20.0
+ * \since 22.10.0
+ * \since 23.4.0
+ *
+ * \pre Bridge is locked
+ *
+ * \param bridge Bridge to operate on.
+ * \param name Name of variable to get.
+ *
+ * \retval Value of the variable on success.
+ * \retval NULL on failure.
+ */
+const char *ast_bridge_get_variable(const struct ast_bridge *bridge, const char *name);
+
+/*!
+ * \brief Get a list of variables that should be included in ARI events for this bridge.
+ * \since 20.20.0
+ * \since 22.10.0
+ * \since 23.4.0
+ *
+ * \pre Bridge is locked
+ *
+ * \param bridge Bridge to operate on.
+ *
+ * \note The returned list must be freed by the caller.
+ *
+ * \retval A pointer to the head of a linked list of variables to include in ARI events on success.
+ * \retval NULL on failure or if no variables are set to be reported.
+ */
+struct varshead *ast_bridge_get_ari_reportable_variables(struct ast_bridge *bridge);
+
struct ast_unreal_pvt;
/*!
*/
int ast_channel_fd_add(struct ast_channel *chan, int value);
+/* ARI reportable variables accessors */
+size_t ast_channel_internal_ari_reportable_vars_count(const struct ast_channel *chan);
+char *ast_channel_internal_ari_reportable_vars_get(const struct ast_channel *chan, size_t index);
+int ast_channel_internal_ari_reportable_vars_append(struct ast_channel *chan, char *key);
+char *ast_channel_internal_ari_reportable_vars_remove(struct ast_channel *chan, size_t index);
+
pthread_t ast_channel_blocker(const struct ast_channel *chan);
void ast_channel_blocker_set(struct ast_channel *chan, pthread_t value);
*/
struct varshead *ast_channel_get_ari_vars(struct ast_channel *chan);
+/*!
+ * \since 20.20.0
+ * \since 22.10.0
+ * \since 23.4.0
+ * \brief Set whether a channel variable should be included in REST events on the channel.
+ *
+ * \param chan Channel to update.
+ * \param variable Variable name or dialplan function expression.
+ * \param report_events Non-zero to include in REST events, zero to omit.
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_channel_set_ari_var_reportable(struct ast_channel *chan, const char *variable, int report_events);
+
/*!
* \since 12
* \brief Gets the variables for a given channel, as set using pbx_builtin_setvar_helper().
*/
int stasis_app_control_set_channel_var(struct stasis_app_control *control, const char *variable, const char *value);
+/*!
+ * \brief Set a variable on the channel associated with this control to value with option of including in events.
+ * \param control Control for \c res_stasis.
+ * \param variable The name of the variable
+ * \param value The value to set the variable to
+ * \param report_events Whether to include this variable in channel events.
+ *
+ * \return 0 for success.
+ * \return -1 for error.
+ */
+int stasis_app_control_set_channel_var_reportable(struct stasis_app_control *control, const char *variable, const char *value, int report_events);
+
+
/*!
* \brief Place the channel associated with the control on hold.
* \param control Control for \c res_stasis.
*/
void stasis_app_bridge_destroy(const char *bridge_id);
+/*!
+ * \brief Set or clear a variable on a bridge and control ARI event reporting for it.
+ *
+ * \param bridge_id Uniqueid of bridge
+ * \param variable Variable name
+ * \param value Variable value (NULL/empty clears)
+ * \param report_events Non-zero to include in ARI bridge events
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int stasis_app_bridge_set_var_reportable(const char *bridge_id, const char *variable,
+ const char *value, int report_events);
+
/*!
* \brief Get the Stasis message sanitizer for app_stasis applications
*
/* Variable name - stores peer information about the most recent attended transfer */
#define ATTENDEDTRANSFER "ATTENDEDTRANSFER"
+/*!
+ * \brief Compare a bridge variable name with a given name.
+ *
+ * \param var_str The variable name to compare.
+ * \param name The name to compare against.
+ *
+ * \retval 0 if the names do not match.
+ * \retval 1 if the names match.
+ */
+#define BV_NAME_CMP(var_str, name) !strcmp(var_str, name)
+
static void cleanup_video_mode(struct ast_bridge *bridge);
/*! Default DTMF keys for built in features */
static void destroy_bridge(void *obj)
{
struct ast_bridge *bridge = obj;
+ struct ast_var_t *var;
ast_debug(1, "Bridge " BRIDGE_PRINTF_SPEC ": actually destroying %s bridge, nobody wants it anymore\n",
BRIDGE_PRINTF_VARS(bridge), bridge->v_table->name);
ast_string_field_free_memory(bridge);
ao2_cleanup(bridge->current_snapshot);
bridge->current_snapshot = NULL;
+ while ((var = AST_LIST_REMOVE_HEAD(&bridge->bridgevars, entries))) {
+ ast_var_delete(var);
+ }
+ AST_LIST_HEAD_INIT_NOLOCK(&bridge->bridgevars);
+ AST_VECTOR_RESET(&bridge->ari_reportable_variable_names, ast_free);
+ AST_VECTOR_FREE(&bridge->ari_reportable_variable_names);
}
struct ast_bridge *bridge_register(struct ast_bridge *bridge)
bridge->v_table = v_table;
AST_VECTOR_INIT(&bridge->media_types, AST_MEDIA_TYPE_END);
+ AST_LIST_HEAD_INIT_NOLOCK(&bridge->bridgevars);
+ AST_VECTOR_INIT(&bridge->ari_reportable_variable_names, 8);
return bridge;
}
ast_channel_stage_snapshot_done(chan);
}
+static int bridge_set_ari_var_reportable(struct ast_bridge *bridge, const char *variable,
+ int report_events)
+{
+ char *var_str;
+
+ if (ast_strlen_zero(variable)) {
+ return -1;
+ }
+
+ if (!report_events) {
+ AST_VECTOR_REMOVE_CMP_UNORDERED(&bridge->ari_reportable_variable_names, variable,
+ BV_NAME_CMP, ast_free);
+ return 0;
+ }
+
+ if (AST_VECTOR_GET_CMP(&bridge->ari_reportable_variable_names, variable, BV_NAME_CMP)) {
+ return 0; /* already present */
+ }
+
+ var_str = ast_strdup(variable);
+ if (!var_str) {
+ return -1;
+ }
+
+ if (AST_VECTOR_APPEND(&bridge->ari_reportable_variable_names, var_str)) {
+ ast_free(var_str);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ast_bridge_set_variable(struct ast_bridge *bridge, const char *name, const char *value,
+ int report_events)
+{
+ struct ast_var_t *var;
+
+ if (ast_strlen_zero(name)) {
+ return -1;
+ }
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&bridge->bridgevars, var, entries) {
+ if (!strcmp(ast_var_name(var), name)) {
+ AST_LIST_REMOVE_CURRENT(entries);
+ ast_var_delete(var);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (ast_strlen_zero(value)) {
+ /* If the value is empty, remove the variable from the reportable list by
+ forcing report_events to 0 */
+ bridge_set_ari_var_reportable(bridge, name, 0);
+ return 0;
+ }
+
+ var = ast_var_assign(name, value);
+ if (!var) {
+ return -1;
+ }
+
+ if (bridge_set_ari_var_reportable(bridge, name, report_events)) {
+ ast_var_delete(var);
+ return -1;
+ }
+
+ AST_LIST_INSERT_TAIL(&bridge->bridgevars, var, entries);
+ return 0;
+}
+
+const char *ast_bridge_get_variable(const struct ast_bridge *bridge, const char *name)
+{
+ return ast_var_find(&bridge->bridgevars, name);
+}
+
+struct varshead *ast_bridge_get_ari_reportable_variables(struct ast_bridge *bridge)
+{
+ struct varshead *ret;
+ char *var_str;
+ size_t i;
+
+ if (AST_VECTOR_SIZE(&bridge->ari_reportable_variable_names) == 0) {
+ return NULL;
+ }
+
+ ret = ast_var_list_create();
+ if (!ret) {
+ return NULL;
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&bridge->ari_reportable_variable_names); ++i) {
+ const char *val = NULL;
+ struct ast_var_t *var;
+
+ var_str = AST_VECTOR_GET(&bridge->ari_reportable_variable_names, i);
+ val = ast_bridge_get_variable(bridge, var_str);
+
+ var = ast_var_assign(var_str, val ? val : "");
+ if (!var) {
+ ast_var_list_destroy(ret);
+ return NULL;
+ }
+
+ AST_LIST_INSERT_TAIL(ret, var, entries);
+ }
+
+ return ret;
+}
+
/*!
* \internal
* \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge.
channel_set_external_vars(&ari_vars, varc, vars);
}
+int ast_channel_set_ari_var_reportable(struct ast_channel *chan, const char *variable, int report_events)
+{
+ char *var_str;
+ size_t i, count;
+
+ SCOPED_CHANNELLOCK(lock, chan);
+
+ if (ast_strlen_zero(variable)) {
+ return -1;
+ }
+
+ count = ast_channel_internal_ari_reportable_vars_count(chan);
+ for (i = 0; i < count; ++i) {
+ var_str = ast_channel_internal_ari_reportable_vars_get(chan, i);
+ if (!strcmp(var_str, variable)) {
+ if (!report_events) {
+ var_str = ast_channel_internal_ari_reportable_vars_remove(chan, i);
+ ast_free(var_str);
+ }
+ return 0;
+ }
+ }
+
+ if (!report_events) {
+ return 0;
+ }
+
+ var_str = ast_strdup(variable);
+ if (!var_str) {
+ return -1;
+ }
+
+ if (ast_channel_internal_ari_reportable_vars_append(chan, var_str)) {
+ ast_free(var_str);
+ return -1;
+ }
+
+ return 0;
+}
+
/*!
* \brief Destructor for lists of variables.
* \param obj AO2 object.
struct varshead *ast_channel_get_ari_vars(struct ast_channel *chan)
{
- return channel_get_external_vars(&ari_vars, chan);
+ RAII_VAR(struct varshead *, ret, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_str *, tmp, NULL, ast_free);
+ char *var_str;
+ size_t i;
+
+ SCOPED_CHANNELLOCK(lock, chan);
+
+ ret = channel_get_external_vars(&ari_vars, chan);
+
+ if (ast_channel_internal_ari_reportable_vars_count(chan) == 0) {
+ if (!ret) {
+ return NULL;
+ }
+
+ ao2_ref(ret, +1);
+ return ret;
+ }
+
+ if (!ret) {
+ ret = ao2_alloc(sizeof(*ret), varshead_dtor);
+ if (!ret) {
+ return NULL;
+ }
+ }
+
+ tmp = ast_str_create(16);
+ if (!tmp) {
+ return NULL;
+ }
+
+ for (i = 0; i < ast_channel_internal_ari_reportable_vars_count(chan); ++i) {
+ const char *val = NULL;
+ struct ast_var_t *var;
+ int already_present = 0;
+ struct ast_var_t *existing;
+
+ var_str = ast_channel_internal_ari_reportable_vars_get(chan, i);
+
+ AST_LIST_TRAVERSE(ret, existing, entries) {
+ if (!strcmp(ast_var_name(existing), var_str)) {
+ already_present = 1;
+ break;
+ }
+ }
+ if (already_present) {
+ continue;
+ }
+
+ if (strchr(var_str, '(')) {
+ if (ast_func_read2(chan, var_str, &tmp, 0) == 0) {
+ val = ast_str_buffer(tmp);
+ } else {
+ ast_log(LOG_ERROR,
+ "Error invoking function %s\n", var_str);
+ }
+ } else {
+ val = pbx_builtin_getvar_helper(chan, var_str);
+ }
+
+ var = ast_var_assign(var_str, val ? val : "");
+ if (!var) {
+ return NULL;
+ }
+
+ AST_RWLIST_INSERT_TAIL(ret, var, entries);
+ }
+
+ ao2_ref(ret, +1);
+ return ret;
}
void ast_channel_close_storage(void)
return AST_VECTOR_SIZE(&chan->fds);
}
+size_t ast_channel_internal_ari_reportable_vars_count(const struct ast_channel *chan)
+{
+ return AST_VECTOR_SIZE(&chan->ari_report_vars);
+}
+
+char *ast_channel_internal_ari_reportable_vars_get(
+ const struct ast_channel *chan, size_t index)
+{
+ return index < AST_VECTOR_SIZE(&chan->ari_report_vars)
+ ? AST_VECTOR_GET(&chan->ari_report_vars, index)
+ : NULL;
+}
+
+int ast_channel_internal_ari_reportable_vars_append(struct ast_channel *chan,
+ char *key)
+{
+ return AST_VECTOR_APPEND(&chan->ari_report_vars, key);
+}
+
+char *ast_channel_internal_ari_reportable_vars_remove(
+ struct ast_channel *chan, size_t index)
+{
+ if (index >= AST_VECTOR_SIZE(&chan->ari_report_vars)) {
+ return NULL;
+ }
+
+ return AST_VECTOR_REMOVE(&chan->ari_report_vars, index, 0);
+}
+
int ast_channel_fd_add(struct ast_channel *chan, int value)
{
int pos = AST_EXTENDED_FDS;
}
AST_VECTOR_INIT(&tmp->fds, AST_MAX_FDS);
+ AST_VECTOR_INIT(&tmp->ari_report_vars, 8);
/* Force all channel snapshot segments to be created on first use, so we don't have to check if
* an old snapshot exists.
ast_channel_internal_set_stream_topology(chan, NULL);
+ AST_VECTOR_RESET(&chan->ari_report_vars, ast_free);
+ AST_VECTOR_FREE(&chan->ari_report_vars);
AST_VECTOR_FREE(&chan->fds);
}
struct ast_flags snapshot_segment_flags; /*!< Flags regarding the segments of the snapshot */
int linked_in_container; /*!< Whether this channel is linked in a storage container */
struct ast_endpoint *endpoint; /*!< The endpoint associated with this channel */
+ AST_VECTOR(, char *) ari_report_vars; /*!< Channel variable names to be included in ARI events */
};
#if defined(__cplusplus) || defined(c_plusplus)
#include "asterisk.h"
#include "asterisk/astobj2.h"
+#include "asterisk/json.h"
#include "asterisk/stasis.h"
#include "asterisk/stasis_cache_pattern.h"
#include "asterisk/channel.h"
ast_string_field_free_memory(snapshot);
ao2_cleanup(snapshot->channels);
snapshot->channels = NULL;
+ ast_var_list_destroy(snapshot->bridgevars);
+ snapshot->bridgevars = NULL;
}
struct ast_bridge_snapshot *ast_bridge_snapshot_create(struct ast_bridge *bridge)
ast_channel_uniqueid(bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc));
}
+ snapshot->bridgevars = ast_bridge_get_ari_reportable_variables(bridge);
+
return snapshot;
}
ast_json_string_create(snapshot->video_source_id));
}
+ if (snapshot->bridgevars && !AST_LIST_EMPTY(snapshot->bridgevars)) {
+ ast_json_object_set(json_bridge, "bridgevars",
+ ast_json_channel_vars(snapshot->bridgevars));
+ }
+
return json_bridge;
}
return;
}
- /*! If there are manager variables, force a cache update */
- if (chan && ast_channel_has_manager_vars()) {
- ast_channel_publish_snapshot(chan);
+ /*! If there are manager or ARI variables, force a cache update */
+ if (chan) {
+ struct varshead *ari_vars = ast_channel_get_ari_vars(chan);
+ if (ast_channel_has_manager_vars() || (ari_vars && !AST_LIST_EMPTY(ari_vars))) {
+ ast_channel_publish_snapshot(chan);
+ }
+ ao2_cleanup(ari_vars);
}
/* This function is NULL safe for global variables */
res = 0;
}
} else
+ if (strcmp("bridgevars", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_object(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Bridge field bridgevars failed validation\n");
+ res = 0;
+ }
+ } else
if (strcmp("channels", ast_json_object_iter_key(iter)) == 0) {
int prop_is_valid;
has_channels = 1;
* Bridge
* - bridge_class: string (required)
* - bridge_type: string (required)
+ * - bridgevars: object
* - channels: List[string] (required)
* - creationtime: Date (required)
* - creator: string (required)
ast_ari_response_ok(response, ast_json_ref(json));
}
+static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables,
+ struct ast_variable **variables, struct ast_variable **report_event_variables)
+{
+ struct ast_json_iter *it_json_var;
+ struct ast_variable *var_tail = NULL;
+ struct ast_variable *report_var_tail = NULL;
+
+ *variables = NULL;
+ *report_event_variables = NULL;
+
+ for (it_json_var = ast_json_object_iter(json_variables); it_json_var;
+ it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
+ struct ast_variable *new_var;
+ const char *key = ast_json_object_iter_key(it_json_var);
+ const char *value = NULL;
+ struct ast_json *json_value = ast_json_object_iter_value(it_json_var);
+ int report_events = 0;
+
+ if (ast_strlen_zero(key)) {
+ continue;
+ }
+
+ if (ast_json_typeof(json_value) == AST_JSON_STRING) {
+ value = ast_json_string_get(json_value);
+ } else if (ast_json_typeof(json_value) == AST_JSON_OBJECT) {
+ struct ast_json *value_field = ast_json_object_get(json_value, "value");
+ struct ast_json *report_field = ast_json_object_get(json_value, "report_events");
+ ast_log(LOG_DEBUG, "Processing variable '%s' with report_events: %s\n", key,
+ report_field ? (ast_json_is_true(report_field) ? "true" : "false") : "not set");
+
+ if (!value_field || ast_json_typeof(value_field) != AST_JSON_STRING) {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Each object value in 'variables' must include string field 'value'");
+ ast_log(LOG_WARNING, "Missing or invalid 'value' field for variable '%s'\n", key);
+ if (!value_field) {
+ ast_log(LOG_WARNING, "Missing 'value' field for variable '%s'\n", key);
+ } else if (ast_json_typeof(value_field) != AST_JSON_STRING) {
+ ast_log(LOG_WARNING, "Invalid 'value' field for variable '%s' (bad type)\n", key);
+ }
+ goto error;
+ }
+
+ value = ast_json_string_get(value_field);
+
+ if (report_field) {
+ enum ast_json_type report_type = ast_json_typeof(report_field);
+
+ if (report_type != AST_JSON_TRUE && report_type != AST_JSON_FALSE) {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Field 'report_events' in 'variables' entries must be boolean");
+ ast_log(LOG_WARNING, "Invalid 'report_events' field for variable '%s' (bad type)\n", key);
+ goto error;
+ }
+
+ report_events = ast_json_is_true(report_field);
+ }
+ } else {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Each value in 'variables' must be a string or an object with 'value' and optional 'report_events'");
+ ast_log(LOG_WARNING, "Invalid value for variable '%s'\n", key);
+ goto error;
+ }
+
+ if (!value) {
+ continue;
+ }
+
+ new_var = ast_variable_new(key, value, "");
+ if (!new_var) {
+ ast_ari_response_alloc_failed(response);
+ goto error;
+ }
+
+ var_tail = ast_variable_list_append_hint(variables, var_tail, new_var);
+
+ if (report_events && report_event_variables) {
+ struct ast_variable *report_var = ast_variable_new(key, "1", "");
+
+ if (!report_var) {
+ ast_ari_response_alloc_failed(response);
+ goto error;
+ }
+
+ report_var_tail = ast_variable_list_append_hint(report_event_variables,
+ report_var_tail, report_var);
+ }
+ }
+
+ return 0;
+
+error:
+ ast_variables_destroy(*variables);
+ *variables = NULL;
+ if (report_event_variables) {
+ ast_variables_destroy(*report_event_variables);
+ *report_event_variables = NULL;
+ }
+ return -1;
+}
+
void ast_ari_bridges_create(struct ast_variable *headers,
struct ast_ari_bridges_create_args *args,
struct ast_ari_response *response)
{
RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
+ struct ast_variable *variables = NULL;
+ struct ast_variable *report_event_variables = NULL;
if (ast_bridge_topic_exists(args->bridge_id)) {
ast_ari_response_error(
return;
}
+ ast_ari_bridges_create_parse_body(args->variables, args);
+
bridge = stasis_app_bridge_create(args->type, args->name, args->bridge_id);
if (!bridge) {
ast_ari_response_error(
return;
}
+ if (args->variables && json_to_ast_variables(response, args->variables,
+ &variables, &report_event_variables)) {
+ return;
+ }
+
ast_bridge_lock(bridge);
+ if (variables) {
+ struct ast_variable *var;
+
+ for (var = variables; var; var = var->next) {
+ int report_events = 0;
+ struct ast_variable *report_var;
+ char buf[strlen(var->name) + 1];
+ char *variable;
+ strcpy(buf, var->name);
+ /* Strip whitespace from the variable name */
+ variable = ast_strip(buf);
+
+ for (report_var = report_event_variables; report_var;
+ report_var = report_var->next) {
+ if (!strcmp(report_var->name, var->name)) {
+ report_events = 1;
+ break;
+ }
+ }
+
+ if (ast_bridge_set_variable(bridge, variable, var->value, report_events)) {
+ ast_bridge_unlock(bridge);
+ ast_variables_destroy(variables);
+ ast_variables_destroy(report_event_variables);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+ }
+ }
snapshot = ast_bridge_snapshot_create(bridge);
ast_bridge_unlock(bridge);
+ ast_variables_destroy(variables);
+ ast_variables_destroy(report_event_variables);
if (!snapshot) {
ast_ari_response_error(
{
RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
+ struct ast_variable *variables = NULL;
+ struct ast_variable *report_event_variables = NULL;
if (ast_bridge_topic_exists(args->bridge_id)) {
ast_ari_response_error(
return;
}
+ ast_ari_bridges_create_with_id_parse_body(args->variables, args);
+
bridge = stasis_app_bridge_create(args->type, args->name, args->bridge_id);
if (!bridge) {
ast_ari_response_error(
return;
}
+ if (args->variables) {
+ struct ast_json *json_variables;
+
+ json_variables = ast_json_object_get(args->variables, "variables");
+ if (json_variables && json_to_ast_variables(response, json_variables,
+ &variables, &report_event_variables)) {
+ if (args->variables) {
+ ast_log(LOG_WARNING, "Failed to parse variables for new bridge '%s'\n", args->bridge_id);
+ } else {
+ ast_log(LOG_WARNING, "Failed to find variables for new bridge '%s'\n", args->bridge_id);
+ }
+ return;
+ }
+ }
+
ast_bridge_lock(bridge);
+ if (variables) {
+ struct ast_variable *var;
+
+ for (var = variables; var; var = var->next) {
+ int report_events = 0;
+ struct ast_variable *report_var;
+ char buf[strlen(var->name) + 1];
+ char *variable;
+ strcpy(buf, var->name);
+ /* Strip whitespace from the variable name */
+ variable = ast_strip(buf);
+
+ report_events = 0;
+ for (report_var = report_event_variables; report_var;
+ report_var = report_var->next) {
+ if (!strcmp(report_var->name, var->name)) {
+ report_events = 1;
+ break;
+ }
+ }
+
+ if (ast_bridge_set_variable(bridge, variable, var->value, report_events)) {
+ ast_bridge_unlock(bridge);
+ ast_variables_destroy(variables);
+ ast_variables_destroy(report_event_variables);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+ }
+ }
snapshot = ast_bridge_snapshot_create(bridge);
ast_bridge_unlock(bridge);
+ ast_variables_destroy(variables);
+ ast_variables_destroy(report_event_variables);
if (!snapshot) {
ast_ari_response_error(
ao2_ref(bridge, -1);
ast_ari_response_no_content(response);
}
+
+void ast_ari_bridges_get_bridge_var(struct ast_variable *headers,
+ struct ast_ari_bridges_get_bridge_var_args *args,
+ struct ast_ari_response *response)
+{
+ RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
+ struct ast_bridge *bridge;
+ const char *value;
+
+ if (ast_strlen_zero(args->variable)) {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Variable name is required");
+ return;
+ }
+
+ bridge = find_bridge(response, args->bridge_id);
+ if (!bridge) {
+ return;
+ }
+
+ ast_bridge_lock(bridge);
+ value = ast_bridge_get_variable(bridge, args->variable);
+ ast_bridge_unlock(bridge);
+
+ if (!value) {
+ ao2_ref(bridge, -1);
+ ast_ari_response_error(response, 404, "Not Found",
+ "Provided variable was not found");
+ return;
+ }
+
+ json = ast_json_pack("{s: s}", "value", value);
+ ao2_ref(bridge, -1);
+
+ if (!json) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ ast_ari_response_ok(response, ast_json_ref(json));
+}
+
+void ast_ari_bridges_set_bridge_var(struct ast_variable *headers,
+ struct ast_ari_bridges_set_bridge_var_args *args,
+ struct ast_ari_response *response)
+{
+ struct ast_bridge *bridge;
+ char buf[strlen(args->variable) + 1];
+ char *variable;
+
+ if (ast_strlen_zero(args->variable)) {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Variable name is required");
+ return;
+ }
+
+ bridge = find_bridge(response, args->bridge_id);
+ if (!bridge) {
+ return;
+ }
+ ao2_ref(bridge, -1);
+
+ strcpy(buf, args->variable);
+ /* Strip whitespace from the variable name */
+ variable = ast_strip(buf);
+
+ if (stasis_app_bridge_set_var_reportable(args->bridge_id, variable, args->value,
+ args->report_events)) {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Failed to execute function");
+ return;
+ }
+
+ ast_ari_response_no_content(response);
+}
+
+void ast_ari_bridges_get_bridge_vars(struct ast_variable *headers,
+ struct ast_ari_bridges_get_bridge_vars_args *args,
+ struct ast_ari_response *response)
+{
+ int res;
+ RAII_VAR(struct ast_json *, json, ast_json_object_create(), ast_json_unref);
+ RAII_VAR(struct ast_json *, inner_json, ast_json_object_create(), ast_json_unref);
+ RAII_VAR(struct ast_str *, value, ast_str_create(32), ast_free);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+
+ ast_assert(response != NULL);
+
+ if (!json || !inner_json || !value) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ if (args->variables_count == 0) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "At least one variable name is required");
+ return;
+ }
+
+ if (ast_strlen_zero(args->bridge_id)) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "Bridge ID is required");
+ return;
+ }
+
+ bridge = stasis_app_bridge_find_by_id(args->bridge_id);
+ if (!bridge) {
+ ast_ari_response_error(
+ response, 404, "Bridge Not Found",
+ "Provided bridge was not found");
+ return;
+ }
+
+ for (int i = 0; i < args->variables_count; i++) {
+ struct ast_json *json_str;
+ char buf[strlen(args->variables[i]) + 1];
+ char *variable;
+ const char *var_value;
+
+ strcpy(buf, args->variables[i]);
+ variable = ast_strip(buf);
+ if (ast_strlen_zero(variable)) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "Variable names are required");
+ return;
+ }
+
+ if (variable[strlen(variable) - 1] == ')') {
+ if (ast_func_read2(NULL, variable, &value, 0)) {
+ ast_ari_response_error(
+ response, 500, "Error With Function",
+ "Unable to read provided function");
+ return;
+ }
+ } else {
+ ast_bridge_lock(bridge);
+ var_value = ast_bridge_get_variable(bridge, variable);
+ ast_bridge_unlock(bridge);
+ if (!var_value) {
+ ast_ari_response_error(
+ response, 404, "Variable Not Found",
+ "Provided variable was not found");
+ return;
+ }
+ ast_str_set(&value, 0, "%s", var_value);
+ }
+
+ json_str = ast_json_string_create(ast_str_buffer(value));
+ if (!json_str) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ res = ast_json_object_set(inner_json, variable, json_str);
+ if (res) {
+ ast_ari_response_alloc_failed(response);
+ ast_json_unref(json_str);
+ return;
+ }
+ }
+
+ res = ast_json_object_set(json, "variables", ast_json_ref(inner_json));
+ if (res) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ ast_ari_response_ok(response, ast_json_ref(json));
+}
+
+void ast_ari_bridges_set_bridge_vars(struct ast_variable *headers,
+ struct ast_ari_bridges_set_bridge_vars_args *args,
+ struct ast_ari_response *response)
+{
+ struct ast_json *json_variables;
+ struct ast_variable *var;
+ RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+ RAII_VAR(struct ast_variable *, report_event_variables, NULL, ast_variables_destroy);
+ RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
+
+ ast_assert(response != NULL);
+
+ if (!args->variables) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "The 'variables' field is required");
+ return;
+ }
+
+ bridge = stasis_app_bridge_find_by_id(args->bridge_id);
+ if (!bridge) {
+ ast_ari_response_error(
+ response, 404, "Bridge Not Found",
+ "Provided bridge was not found");
+ return;
+ }
+
+ json_variables = ast_json_object_get(args->variables, "variables");
+ if (!json_variables || ast_json_typeof(json_variables) != AST_JSON_OBJECT) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "The 'variables' field must be a JSON object");
+ return;
+ }
+
+ if (json_to_ast_variables(response, json_variables, &variables,
+ &report_event_variables)) {
+ return;
+ }
+
+ for (var = variables; var; var = var->next) {
+ int report_events = 0;
+ struct ast_variable *report_var;
+ char buf[strlen(var->name) + 1];
+ char *variable;
+ strcpy(buf, var->name);
+ /* Strip whitespace from the variable name */
+ variable = ast_strip(buf);
+
+ /* See if the variable is in the report event list */
+ for (report_var = report_event_variables; report_var;
+ report_var = report_var->next) {
+ if (!strcmp(report_var->name, var->name)) {
+ report_events = 1;
+ break;
+ }
+ }
+
+ if (stasis_app_bridge_set_var_reportable(args->bridge_id, variable, var->value,
+ report_events)) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "Failed to execute function");
+ return;
+ }
+ }
+
+ ast_ari_response_no_content(response);
+}
const char *bridge_id;
/*! Name to give to the bridge being created. */
const char *name;
+ /*! The "variables" key in the body object holds variable key/value pairs to set on the bridge on creation. Each variable is an object containing "value" (string) and optional "report_events" (boolean) to include updates for that variable in bridge events (defaults to false). Ex. { "name": "SupportBridge", "variables": { "Bridge_State": { "value": "WaitingForAgent", "report_events": true } } } */
+ struct ast_json *variables;
};
/*!
* \brief Body parsing function for /bridges.
const char *bridge_id;
/*! Set the name of the bridge. */
const char *name;
+ /*! The "variables" key in the body object holds variable key/value pairs to set on the bridge on creation. Each variable is an object containing "value" (string) and optional "report_events" (boolean) to include updates for that variable in bridge events (defaults to false). Ex. { "name": "SupportBridge", "variables": { "Bridge_State": { "value": "WaitingForAgent", "report_events": true } } } */
+ struct ast_json *variables;
};
/*!
* \brief Body parsing function for /bridges/{bridgeId}.
* \param[out] response HTTP response
*/
void ast_ari_bridges_destroy(struct ast_variable *headers, struct ast_ari_bridges_destroy_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_bridges_get_bridge_var() */
+struct ast_ari_bridges_get_bridge_var_args {
+ /*! Bridge's id */
+ const char *bridge_id;
+ /*! The bridge variable or function to get */
+ const char *variable;
+};
+/*!
+ * \brief Body parsing function for /bridges/{bridgeId}/variable.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_bridges_get_bridge_var_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_get_bridge_var_args *args);
+
+/*!
+ * \brief Get the value of a bridge variable or function.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_bridges_get_bridge_var(struct ast_variable *headers, struct ast_ari_bridges_get_bridge_var_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_bridges_set_bridge_var() */
+struct ast_ari_bridges_set_bridge_var_args {
+ /*! Bridge's id */
+ const char *bridge_id;
+ /*! The bridge variable or function to set */
+ const char *variable;
+ /*! The value to set the variable to */
+ const char *value;
+ /*! Whether this variable should be included in bridge events. Defaults to false. */
+ int report_events;
+};
+/*!
+ * \brief Body parsing function for /bridges/{bridgeId}/variable.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_bridges_set_bridge_var_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_set_bridge_var_args *args);
+
+/*!
+ * \brief Set the value of a bridge variable or function.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_bridges_set_bridge_var(struct ast_variable *headers, struct ast_ari_bridges_set_bridge_var_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_bridges_get_bridge_vars() */
+struct ast_ari_bridges_get_bridge_vars_args {
+ /*! Bridge's id */
+ const char *bridge_id;
+ /*! Array of The bridge variables or functions to get */
+ const char **variables;
+ /*! Length of variables array. */
+ size_t variables_count;
+ /*! Parsing context for variables. */
+ char *variables_parse;
+};
+/*!
+ * \brief Body parsing function for /bridges/{bridgeId}/variables.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_bridges_get_bridge_vars_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_get_bridge_vars_args *args);
+
+/*!
+ * \brief Get the value of multiple bridge variables or functions.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_bridges_get_bridge_vars(struct ast_variable *headers, struct ast_ari_bridges_get_bridge_vars_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_bridges_set_bridge_vars() */
+struct ast_ari_bridges_set_bridge_vars_args {
+ /*! Bridge's id */
+ const char *bridge_id;
+ /*! The "variables" key in the body object holds variable key/value pairs to set on the bridge. Each variable value may be either a string or an object containing "value" (string) and optional "report_events" (boolean) to include updates for that variable in bridge events (defaults to false). Ex. { "variables": { "Bridge_State": "WaitingForAgent", "Support_Level": { "value": "Premium", "report_events": true } } } */
+ struct ast_json *variables;
+};
+/*!
+ * \brief Body parsing function for /bridges/{bridgeId}/variables.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_bridges_set_bridge_vars_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_set_bridge_vars_args *args);
+
+/*!
+ * \brief Set the values of multiple bridge variables or functions.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_bridges_set_bridge_vars(struct ast_variable *headers, struct ast_ari_bridges_set_bridge_vars_args *args, struct ast_ari_response *response);
/*! Argument struct for ast_ari_bridges_add_channel() */
struct ast_ari_bridges_add_channel_args {
/*! Bridge's id */
const char *args_caller_id,
int args_timeout,
struct ast_variable *variables,
+ struct ast_variable *report_event_variables,
const char *args_channel_id,
const char *args_other_channel_id,
const char *args_originator,
}
}
+ if (report_event_variables) {
+ struct ast_variable *var;
+
+ for (var = report_event_variables; var; var = var->next) {
+ if (ast_channel_set_ari_var_reportable(chan, var->name, 1)) {
+ ast_ari_response_alloc_failed(response);
+ ast_dial_destroy(dial);
+ ast_free(origination);
+ return NULL;
+ }
+ }
+ }
snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
/* Before starting the async dial bump the ref in case the dial quickly goes away and takes
* \retval 0 on success.
* \retval -1 on error.
*/
-static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
+static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables,
+ struct ast_variable **variables, struct ast_variable **report_event_variables)
{
- enum ast_json_to_ast_vars_code res;
+ struct ast_json_iter *it_json_var;
+ struct ast_variable *var_tail = NULL;
+ struct ast_variable *report_var_tail = NULL;
- res = ast_json_to_ast_variables(json_variables, variables);
- switch (res) {
- case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
- return 0;
- case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
- ast_ari_response_error(response, 400, "Bad Request",
- "Only string values in the 'variables' object allowed");
- break;
- case AST_JSON_TO_AST_VARS_CODE_OOM:
- ast_ari_response_alloc_failed(response);
- break;
+ *variables = NULL;
+ *report_event_variables = NULL;
+
+ for (it_json_var = ast_json_object_iter(json_variables); it_json_var;
+ it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
+ struct ast_variable *new_var;
+ const char *key = ast_json_object_iter_key(it_json_var);
+ const char *value = NULL;
+ struct ast_json *json_value = ast_json_object_iter_value(it_json_var);
+ int report_events = 0;
+
+ if (ast_strlen_zero(key)) {
+ continue;
+ }
+
+ if (ast_json_typeof(json_value) == AST_JSON_STRING) {
+ value = ast_json_string_get(json_value);
+ } else if (ast_json_typeof(json_value) == AST_JSON_OBJECT) {
+ struct ast_json *value_field = ast_json_object_get(json_value, "value");
+ struct ast_json *report_field = ast_json_object_get(json_value, "report_events");
+ ast_log(LOG_DEBUG, "Processing variable '%s' with report_events: %s\n", key,
+ report_field ? (ast_json_is_true(report_field) ? "true" : "false") : "not set");
+
+ if (!value_field || ast_json_typeof(value_field) != AST_JSON_STRING) {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Each object value in 'variables' must include string field 'value'");
+ goto error;
+ }
+
+ value = ast_json_string_get(value_field);
+ ast_log(LOG_DEBUG, "Variable '%s' has value '%s'\n", key, value);
+
+ if (report_field) {
+ enum ast_json_type report_type = ast_json_typeof(report_field);
+
+ if (report_type != AST_JSON_TRUE && report_type != AST_JSON_FALSE) {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Field 'report_events' in 'variables' entries must be boolean");
+ goto error;
+ }
+
+ report_events = ast_json_is_true(report_field);
+ }
+ } else {
+ ast_ari_response_error(response, 400, "Bad Request",
+ "Each value in 'variables' must be a string or an object with 'value' and optional 'report_events'");
+ goto error;
+ }
+
+ if (!value) {
+ continue;
+ }
+
+ new_var = ast_variable_new(key, value, "");
+ if (!new_var) {
+ ast_ari_response_alloc_failed(response);
+ goto error;
+ }
+
+ var_tail = ast_variable_list_append_hint(variables, var_tail, new_var);
+
+ if (report_events) {
+ struct ast_variable *report_var = ast_variable_new(key, "1", "");
+
+ if (!report_var) {
+ ast_ari_response_alloc_failed(response);
+ goto error;
+ }
+
+ report_var_tail = ast_variable_list_append_hint(report_event_variables,
+ report_var_tail, report_var);
+ }
}
- ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
+ return 0;
+
+error:
+ ast_variables_destroy(*variables);
+ *variables = NULL;
+ ast_variables_destroy(*report_event_variables);
+ *report_event_variables = NULL;
return -1;
}
struct ast_ari_response *response)
{
struct ast_variable *variables = NULL;
+ struct ast_variable *report_event_variables = NULL;
struct ast_channel *chan;
/* Parse any query parameters out of the body parameter */
ast_ari_channels_originate_with_id_parse_body(args->variables, args);
json_variables = ast_json_object_get(args->variables, "variables");
if (json_variables
- && json_to_ast_variables(response, json_variables, &variables)) {
+ && json_to_ast_variables(response, json_variables, &variables,
+ &report_event_variables)) {
return;
}
}
args->caller_id,
args->timeout,
variables,
+ report_event_variables,
args->channel_id,
args->other_channel_id,
args->originator,
args->formats,
response);
ast_channel_cleanup(chan);
+ ast_variables_destroy(report_event_variables);
ast_variables_destroy(variables);
}
struct ast_ari_response *response)
{
struct ast_variable *variables = NULL;
+ struct ast_variable *report_event_variables = NULL;
struct ast_channel *chan;
/* Parse any query parameters out of the body parameter */
ast_ari_channels_originate_parse_body(args->variables, args);
json_variables = ast_json_object_get(args->variables, "variables");
if (json_variables
- && json_to_ast_variables(response, json_variables, &variables)) {
+ && json_to_ast_variables(response, json_variables, &variables,
+ &report_event_variables)) {
return;
}
}
args->caller_id,
args->timeout,
variables,
+ report_event_variables,
args->channel_id,
args->other_channel_id,
args->originator,
args->formats,
response);
ast_channel_cleanup(chan);
+ ast_variables_destroy(report_event_variables);
ast_variables_destroy(variables);
}
return;
}
- if (stasis_app_control_set_channel_var(control, args->variable, args->value)) {
+ if (stasis_app_control_set_channel_var_reportable(control, args->variable, args->value,
+ args->report_events)) {
ast_ari_response_error(
response, 400, "Bad Request",
"Failed to execute function");
struct ast_ari_response *response)
{
struct ast_json *json_variables;
- struct ast_json_iter *it_json_var;
- struct ast_variable *var = NULL;
+ struct ast_variable *var;
RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+ RAII_VAR(struct ast_variable *, report_event_variables, NULL, ast_variables_destroy);
RAII_VAR(struct ast_channel *, channel, NULL, ast_channel_cleanup);
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
}
json_variables = ast_json_object_get(args->variables, "variables");
- for (it_json_var = ast_json_object_iter(json_variables); it_json_var;
- it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
- const char *key = ast_json_object_iter_key(it_json_var);
- char buf[strlen(key) + 1];
- char *stripped_key;
- struct ast_json *json_value;
- const char *value;
- struct ast_variable *new_var;
-
- strcpy(buf, key);
- stripped_key = ast_strip(buf);
- if (ast_strlen_zero(stripped_key)) {
- ast_ari_response_error(
- response, 400, "Bad Request",
- "Variable names are required");
- return;
- }
+ if (!json_variables || ast_json_typeof(json_variables) != AST_JSON_OBJECT) {
+ ast_ari_response_error(
+ response, 400, "Bad Request",
+ "The 'variables' field must be a JSON object");
+ return;
+ }
- json_value = ast_json_object_iter_value(it_json_var);
- if (ast_json_typeof(json_value) != AST_JSON_STRING) {
- ast_ari_response_error(
- response, 400, "Bad Request",
- "Variable values must be strings");
- return;
- }
+ if (json_to_ast_variables(response, json_variables, &variables,
+ &report_event_variables)) {
+ return;
+ }
- value = ast_json_string_get(json_value);
- if (!value) {
- ast_ari_response_error(
- response, 500, "Internal Server Error",
- "Could not get string value from JSON string");
- return;
- }
+ for (var = variables; var; var = var->next) {
+ int report_events = 0;
+ struct ast_variable *report_var;
+ char buf[strlen(var->name) + 1];
+ char *variable;
+ strcpy(buf, var->name);
+ /* Strip whitespace from the variable name */
+ variable = ast_strip(buf);
- new_var = ast_variable_new(stripped_key, value, "");
- if (!new_var) {
- ast_ari_response_error(
- response, 500, "Internal Server Error",
- "Could not create internal variable");
- return;
+ /* See if the variable is in the report event list */
+ for (report_var = report_event_variables; report_var;
+ report_var = report_var->next) {
+ if (!strcmp(report_var->name, var->name)) {
+ report_events = 1;
+ break;
+ }
}
- /* Append to the tail */
- var = ast_variable_list_append_hint(&variables, var, new_var);
- }
-
- /* We loop twice to preserve variable state if something goes wrong. If something
- * goes wrong in this loop, something went VERY wrong.
- */
- for (var = variables; var; var = var->next) {
- if (stasis_app_control_set_channel_var(control, var->name, var->value)) {
+ if (stasis_app_control_set_channel_var_reportable(control, variable,
+ var->value, report_events)) {
ast_ari_response_error(
response, 400, "Bad Request",
"Failed to execute function");
struct ast_ari_response *response)
{
RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+ RAII_VAR(struct ast_variable *, report_event_variables, NULL, ast_variables_destroy);
struct ast_assigned_ids assignedids;
struct ari_channel_thread_data *chan_data;
struct ast_channel_snapshot *snapshot;
ast_ari_channels_create_parse_body(args->variables, args);
json_variables = ast_json_object_get(args->variables, "variables");
- if (json_variables
- && json_to_ast_variables(response, json_variables, &variables)) {
+ if (json_variables &&
+ json_to_ast_variables(response, json_variables, &variables, &report_event_variables)) {
+ ast_log(LOG_ERROR, "Failed to parse variables from request body for channel creation\n");
return;
}
}
if (variables) {
ast_set_variables(chan_data->chan, variables);
}
+ if (report_event_variables) {
+ struct ast_variable *var;
+
+ for (var = report_event_variables; var; var = var->next) {
+ if (ast_channel_set_ari_var_reportable(chan_data->chan, var->name, 1)) {
+ ast_ari_response_alloc_failed(response);
+ ast_channel_cleanup(originator);
+ chan_data_destroy(chan_data);
+ return;
+ }
+ }
+ }
ast_channel_cleanup(originator);
static int external_media_rtp_udp(struct ast_ari_channels_external_media_args *args,
struct ast_variable *variables,
+ struct ast_variable *report_event_variables,
struct ast_ari_response *response)
{
char *endpoint;
NULL,
0,
variables,
+ report_event_variables,
args->channel_id,
NULL,
NULL,
static int external_media_audiosocket_tcp(struct ast_ari_channels_external_media_args *args,
struct ast_variable *variables,
+ struct ast_variable *report_event_variables,
struct ast_ari_response *response)
{
char *endpoint;
NULL,
0,
variables,
+ report_event_variables,
args->channel_id,
NULL,
NULL,
static int external_media_websocket(struct ast_ari_channels_external_media_args *args,
struct ast_variable *variables,
+ struct ast_variable *report_event_variables,
struct ast_ari_response *response)
{
char *endpoint;
NULL,
0,
variables,
+ report_event_variables,
args->channel_id,
NULL,
NULL,
struct ast_ari_channels_external_media_args *args, struct ast_ari_response *response)
{
RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+ RAII_VAR(struct ast_variable *, report_event_variables, NULL, ast_variables_destroy);
char *external_host;
char *host = NULL;
char *port = NULL;
ast_ari_channels_external_media_parse_body(args->variables, args);
json_variables = ast_json_object_get(args->variables, "variables");
if (json_variables
- && json_to_ast_variables(response, json_variables, &variables)) {
+ && json_to_ast_variables(response, json_variables, &variables,
+ &report_event_variables)) {
return;
}
}
}
if (strcasecmp(args->encapsulation, "rtp") == 0 && strcasecmp(args->transport, "udp") == 0) {
- if (external_media_rtp_udp(args, variables, response)) {
+ if (external_media_rtp_udp(args, variables, report_event_variables, response)) {
ast_ari_response_error(
response, 500, "Internal Server Error",
"An internal error prevented this request from being handled");
} else if (strcasecmp(args->encapsulation, "audiosocket") == 0 && strcasecmp(args->transport, "tcp") == 0) {
if (ast_strlen_zero(args->data)) {
ast_ari_response_error(response, 400, "Bad Request", "data can not be empty");
- } else if (external_media_audiosocket_tcp(args, variables, response)) {
+ } else if (external_media_audiosocket_tcp(args, variables, report_event_variables, response)) {
ast_ari_response_error(
response, 500, "Internal Server Error",
"An internal error prevented this request from being handled");
}
} else if (strcasecmp(args->encapsulation, "none") == 0 && strcasecmp(args->transport, "websocket") == 0) {
- if (external_media_websocket(args, variables, response)) {
+ if (external_media_websocket(args, variables, report_event_variables, response)) {
ast_ari_response_error(
response, 500, "Internal Server Error",
"An internal error prevented this request from being handled");
const char *caller_id;
/*! Timeout (in seconds) before giving up dialing, or -1 for no timeout. */
int timeout;
- /*! The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice" } } */
+ /*! The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Each variable value may be either a string or an object containing "value" (string) and optional "report_events" (boolean) to include updates for that variable in channel events (defaults to false). Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice", "Call_State": { "value": "WaitingForAgent", "report_events": true } } } */
struct ast_json *variables;
/*! The unique id to assign the channel on creation. */
const char *channel_id;
const char *originator;
/*! The format name capability list to use if originator is not specified. Ex. "ulaw,slin16". Format names can be found with "core show codecs". */
const char *formats;
- /*! The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice" } } */
+ /*! The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Each variable value may be either a string or an object containing "value" (string) and optional "report_events" (boolean) to include updates for that variable in channel events (defaults to false). Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice", "Call_State": { "value": "WaitingForAgent", "report_events": true } } } */
struct ast_json *variables;
};
/*!
const char *caller_id;
/*! Timeout (in seconds) before giving up dialing, or -1 for no timeout. */
int timeout;
- /*! The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice" } } */
+ /*! The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Each variable value may be either a string or an object containing "value" (string) and optional "report_events" (boolean) to include updates for that variable in channel events (defaults to false). Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice", "Call_State": { "value": "WaitingForAgent", "report_events": true } } } */
struct ast_json *variables;
/*! The unique id to assign the second channel when using local channels. */
const char *other_channel_id;
const char *variable;
/*! The value to set the variable to */
const char *value;
+ /*! Whether this variable should be included in channel events. Defaults to false. */
+ int report_events;
};
/*!
* \brief Body parsing function for /channels/{channelId}/variable.
struct ast_ari_channels_set_channel_vars_args {
/*! Channel's id */
const char *channel_id;
- /*! The "variables" key in the body object holds variable key/value pairs to set on the channel. Ex. { "variables": { "CALLERID(name)": "Alice" } } */
+ /*! The "variables" key in the body object holds variable key/value pairs to set on the channel. Each variable value may be either a string or an object containing "value" (string) and optional "report_events" (boolean) to include updates for that variable in channel events (defaults to false). Ex. { "variables": { "CALLERID(name)": "Alice", "Call_State": { "value": "WaitingForAgent", "report_events": true } } } */
struct ast_json *variables;
};
/*!
const char *channel_id;
/*! Stasis Application to place channel into */
const char *app;
- /*! The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice" } } */
+ /*! The "variables" key in the body object holds variable key/value pairs to set on the channel on creation. Each variable value may be either a string or an object containing "value" (string) and optional "report_events" (boolean) to include updates for that variable in channel events (defaults to false). Other keys in the body object are interpreted as query parameters. Ex. { "endpoint": "SIP/Alice", "variables": { "CALLERID(name)": "Alice", "Call_State": { "value": "WaitingForAgent", "report_events": true } } } */
struct ast_json *variables;
/*! Hostname/ip:port or websocket_client connection ID of external host. May be empty for a websocket server connection. */
const char *external_host;
} else
{}
}
- if (ast_ari_bridges_create_parse_body(body, &args)) {
- ast_ari_response_alloc_failed(response);
- goto fin;
- }
+ args.variables = body;
ast_ari_bridges_create(headers, &args, response);
#if defined(AST_DEVMODE)
code = response->response_code;
} else
{}
}
- if (ast_ari_bridges_create_with_id_parse_body(body, &args)) {
- ast_ari_response_alloc_failed(response);
- goto fin;
- }
+ args.variables = body;
ast_ari_bridges_create_with_id(headers, &args, response);
#if defined(AST_DEVMODE)
code = response->response_code;
}
#endif /* AST_DEVMODE */
+fin: __attribute__((unused))
+ return;
+}
+int ast_ari_bridges_get_bridge_var_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_get_bridge_var_args *args)
+{
+ struct ast_json *field;
+ /* Parse query parameters out of it */
+ field = ast_json_object_get(body, "variable");
+ if (field) {
+ args->variable = ast_json_string_get(field);
+ }
+ return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /bridges/{bridgeId}/variable.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_bridges_get_bridge_var_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+ struct ast_ari_bridges_get_bridge_var_args args = {};
+ struct ast_variable *i;
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = get_params; i; i = i->next) {
+ if (strcmp(i->name, "variable") == 0) {
+ args.variable = (i->value);
+ } else
+ {}
+ }
+ for (i = path_vars; i; i = i->next) {
+ if (strcmp(i->name, "bridgeId") == 0) {
+ args.bridge_id = (i->value);
+ } else
+ {}
+ }
+ if (ast_ari_bridges_get_bridge_var_parse_body(body, &args)) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+ ast_ari_bridges_get_bridge_var(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ case 400: /* Missing variable parameter. */
+ case 404: /* Bridge or variable not found */
+ case 409: /* Bridge not in a Stasis application */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_variable(
+ response->message);
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/variable\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/variable\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+ return;
+}
+int ast_ari_bridges_set_bridge_var_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_set_bridge_var_args *args)
+{
+ struct ast_json *field;
+ /* Parse query parameters out of it */
+ field = ast_json_object_get(body, "variable");
+ if (field) {
+ args->variable = ast_json_string_get(field);
+ }
+ field = ast_json_object_get(body, "value");
+ if (field) {
+ args->value = ast_json_string_get(field);
+ }
+ field = ast_json_object_get(body, "report_events");
+ if (field) {
+ args->report_events = ast_json_is_true(field);
+ }
+ return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /bridges/{bridgeId}/variable.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_bridges_set_bridge_var_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+ struct ast_ari_bridges_set_bridge_var_args args = {};
+ struct ast_variable *i;
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = get_params; i; i = i->next) {
+ if (strcmp(i->name, "variable") == 0) {
+ args.variable = (i->value);
+ } else
+ if (strcmp(i->name, "value") == 0) {
+ args.value = (i->value);
+ } else
+ if (strcmp(i->name, "report_events") == 0) {
+ args.report_events = ast_true(i->value);
+ } else
+ {}
+ }
+ for (i = path_vars; i; i = i->next) {
+ if (strcmp(i->name, "bridgeId") == 0) {
+ args.bridge_id = (i->value);
+ } else
+ {}
+ }
+ if (ast_ari_bridges_set_bridge_var_parse_body(body, &args)) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+ ast_ari_bridges_set_bridge_var(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ case 400: /* Missing variable parameter. */
+ case 404: /* Bridge not found */
+ case 409: /* Bridge not in a Stasis application */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_void(
+ response->message);
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/variable\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/variable\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+ return;
+}
+int ast_ari_bridges_get_bridge_vars_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_get_bridge_vars_args *args)
+{
+ struct ast_json *field;
+ /* Parse query parameters out of it */
+ field = ast_json_object_get(body, "variables");
+ if (field) {
+ /* If they were silly enough to both pass in a query param and a
+ * JSON body, free up the query value.
+ */
+ ast_free(args->variables);
+ if (ast_json_typeof(field) == AST_JSON_ARRAY) {
+ /* Multiple param passed as array */
+ size_t i;
+ args->variables_count = ast_json_array_size(field);
+ args->variables = ast_malloc(sizeof(*args->variables) * args->variables_count);
+
+ if (!args->variables) {
+ return -1;
+ }
+
+ for (i = 0; i < args->variables_count; ++i) {
+ args->variables[i] = ast_json_string_get(ast_json_array_get(field, i));
+ }
+ } else {
+ /* Multiple param passed as single value */
+ args->variables_count = 1;
+ args->variables = ast_malloc(sizeof(*args->variables) * args->variables_count);
+ if (!args->variables) {
+ return -1;
+ }
+ args->variables[0] = ast_json_string_get(field);
+ }
+ }
+ return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /bridges/{bridgeId}/variables.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_bridges_get_bridge_vars_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+ struct ast_ari_bridges_get_bridge_vars_args args = {};
+ struct ast_variable *i;
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = get_params; i; i = i->next) {
+ if (strcmp(i->name, "variables") == 0) {
+ /* Parse comma separated list */
+ char *vals[MAX_VALS];
+ size_t j;
+
+ args.variables_parse = ast_strdup(i->value);
+ if (!args.variables_parse) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ if (strlen(args.variables_parse) == 0) {
+ /* ast_app_separate_args can't handle "" */
+ args.variables_count = 1;
+ vals[0] = args.variables_parse;
+ } else {
+ args.variables_count = ast_app_separate_args(
+ args.variables_parse, ',', vals,
+ ARRAY_LEN(vals));
+ }
+
+ if (args.variables_count == 0) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ if (args.variables_count >= MAX_VALS) {
+ ast_ari_response_error(response, 400,
+ "Bad Request",
+ "Too many values for variables");
+ goto fin;
+ }
+
+ args.variables = ast_malloc(sizeof(*args.variables) * args.variables_count);
+ if (!args.variables) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+
+ for (j = 0; j < args.variables_count; ++j) {
+ args.variables[j] = (vals[j]);
+ }
+ } else
+ {}
+ }
+ for (i = path_vars; i; i = i->next) {
+ if (strcmp(i->name, "bridgeId") == 0) {
+ args.bridge_id = (i->value);
+ } else
+ {}
+ }
+ if (ast_ari_bridges_get_bridge_vars_parse_body(body, &args)) {
+ ast_ari_response_alloc_failed(response);
+ goto fin;
+ }
+ ast_ari_bridges_get_bridge_vars(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ case 400: /* Missing variables parameter. */
+ case 404: /* Bridge or variable not found */
+ case 409: /* Bridge not in a Stasis application */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_variables(
+ response->message);
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/variables\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/variables\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+ ast_free(args.variables_parse);
+ ast_free(args.variables);
+ return;
+}
+int ast_ari_bridges_set_bridge_vars_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_set_bridge_vars_args *args)
+{
+ /* Parse query parameters out of it */
+ return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /bridges/{bridgeId}/variables.
+ * \param ser TCP/TLS session object
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param body
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_bridges_set_bridge_vars_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
+{
+ struct ast_ari_bridges_set_bridge_vars_args args = {};
+ struct ast_variable *i;
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = path_vars; i; i = i->next) {
+ if (strcmp(i->name, "bridgeId") == 0) {
+ args.bridge_id = (i->value);
+ } else
+ {}
+ }
+ args.variables = body;
+ ast_ari_bridges_set_bridge_vars(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ case 400: /* Missing variables parameter. */
+ case 404: /* Bridge not found */
+ case 409: /* Bridge not in a Stasis application */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_void(
+ response->message);
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/variables\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/variables\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
fin: __attribute__((unused))
return;
}
return;
}
+/*! \brief REST handler for /api-docs/bridges.json */
+static struct stasis_rest_handlers bridges_bridgeId_variable = {
+ .path_segment = "variable",
+ .callbacks = {
+ [AST_HTTP_GET] = ast_ari_bridges_get_bridge_var_cb,
+ [AST_HTTP_POST] = ast_ari_bridges_set_bridge_var_cb,
+ },
+ .num_children = 0,
+ .children = { }
+};
+/*! \brief REST handler for /api-docs/bridges.json */
+static struct stasis_rest_handlers bridges_bridgeId_variables = {
+ .path_segment = "variables",
+ .callbacks = {
+ [AST_HTTP_GET] = ast_ari_bridges_get_bridge_vars_cb,
+ [AST_HTTP_POST] = ast_ari_bridges_set_bridge_vars_cb,
+ },
+ .num_children = 0,
+ .children = { }
+};
/*! \brief REST handler for /api-docs/bridges.json */
static struct stasis_rest_handlers bridges_bridgeId_addChannel = {
.path_segment = "addChannel",
[AST_HTTP_GET] = ast_ari_bridges_get_cb,
[AST_HTTP_DELETE] = ast_ari_bridges_destroy_cb,
},
- .num_children = 6,
- .children = { &bridges_bridgeId_addChannel,&bridges_bridgeId_removeChannel,&bridges_bridgeId_videoSource,&bridges_bridgeId_moh,&bridges_bridgeId_play,&bridges_bridgeId_record, }
+ .num_children = 8,
+ .children = { &bridges_bridgeId_variable,&bridges_bridgeId_variables,&bridges_bridgeId_addChannel,&bridges_bridgeId_removeChannel,&bridges_bridgeId_videoSource,&bridges_bridgeId_moh,&bridges_bridgeId_play,&bridges_bridgeId_record, }
};
/*! \brief REST handler for /api-docs/bridges.json */
static struct stasis_rest_handlers bridges = {
if (field) {
args->value = ast_json_string_get(field);
}
+ field = ast_json_object_get(body, "report_events");
+ if (field) {
+ args->report_events = ast_json_is_true(field);
+ }
return 0;
}
if (strcmp(i->name, "value") == 0) {
args.value = (i->value);
} else
+ if (strcmp(i->name, "report_events") == 0) {
+ args.report_events = ast_true(i->value);
+ } else
{}
}
for (i = path_vars; i; i = i->next) {
ast_bridge_destroy(bridge, 0);
}
+int stasis_app_bridge_set_var_reportable(const char *bridge_id, const char *variable,
+ const char *value, int report_events)
+{
+ RAII_VAR(struct ast_bridge *, bridge, stasis_app_bridge_find_by_id(bridge_id), ao2_cleanup);
+
+ if (!bridge) {
+ return -1;
+ }
+
+ ast_bridge_lock(bridge);
+ if (ast_bridge_set_variable(bridge, variable, value, report_events)) {
+ ast_bridge_unlock(bridge);
+ return -1;
+ }
+ ast_bridge_publish_state(bridge);
+ ast_bridge_unlock(bridge);
+
+ return 0;
+}
+
struct replace_channel_store {
struct ast_channel_snapshot *snapshot;
char *app;
char *name;
/*! Value of variable to set. If unsetting, this will be NULL */
char *value;
+ /*! Whether this variable should be included in channel events */
+ unsigned int report_events;
};
static void free_chanvar(void *data)
{
struct chanvar *var = data;
+ if (ast_channel_set_ari_var_reportable(control->channel, var->name, var->report_events)) {
+ return -1;
+ }
+
pbx_builtin_setvar_helper(control->channel, var->name, var->value);
return 0;
}
int stasis_app_control_set_channel_var(struct stasis_app_control *control, const char *variable, const char *value)
+{
+ return stasis_app_control_set_channel_var_reportable(control, variable, value, 0);
+}
+
+int stasis_app_control_set_channel_var_reportable(struct stasis_app_control *control, const char *variable, const char *value, int report_events)
{
struct chanvar *var;
}
}
- stasis_app_send_command_async(control, app_control_set_channel_var, var, free_chanvar);
+ var->report_events = report_events ? 1 : 0;
- return 0;
+ return stasis_app_send_command(control, app_control_set_channel_var, var, free_chanvar);
}
static int app_control_hold(struct stasis_app_control *control,
"required": false,
"allowMultiple": false,
"dataType": "string"
+ },
+ {
+ "name": "variables",
+ "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the bridge on creation. Each variable is an object containing \"value\" (string) and optional \"report_events\" (boolean) to include updates for that variable in bridge events (defaults to false). Ex. { \"name\": \"SupportBridge\", \"variables\": { \"Bridge_State\": { \"value\": \"WaitingForAgent\", \"report_events\": true } } }",
+ "paramType": "body",
+ "required": false,
+ "dataType": "containers",
+ "allowMultiple": false
}
],
"errorResponses": [
"required": false,
"allowMultiple": false,
"dataType": "string"
+ },
+ {
+ "name": "variables",
+ "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the bridge on creation. Each variable is an object containing \"value\" (string) and optional \"report_events\" (boolean) to include updates for that variable in bridge events (defaults to false). Ex. { \"name\": \"SupportBridge\", \"variables\": { \"Bridge_State\": { \"value\": \"WaitingForAgent\", \"report_events\": true } } }",
+ "paramType": "body",
+ "required": false,
+ "dataType": "containers",
+ "allowMultiple": false
}
],
"errorResponses": [
}
]
},
+ {
+ "path": "/bridges/{bridgeId}/variable",
+ "description": "Variables on a bridge",
+ "operations": [
+ {
+ "httpMethod": "GET",
+ "since": [
+ "20.20.0",
+ "22.10.0",
+ "23.4.0"
+ ],
+ "summary": "Get the value of a bridge variable or function.",
+ "nickname": "getBridgeVar",
+ "responseClass": "Variable",
+ "parameters": [
+ {
+ "name": "bridgeId",
+ "description": "Bridge's id",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "variable",
+ "description": "The bridge variable or function to get",
+ "paramType": "query",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 400,
+ "reason": "Missing variable parameter."
+ },
+ {
+ "code": 404,
+ "reason": "Bridge or variable not found"
+ },
+ {
+ "code": 409,
+ "reason": "Bridge not in a Stasis application"
+ }
+ ]
+ },
+ {
+ "httpMethod": "POST",
+ "since": [
+ "20.20.0",
+ "22.10.0",
+ "23.4.0"
+ ],
+ "summary": "Set the value of a bridge variable or function.",
+ "nickname": "setBridgeVar",
+ "responseClass": "void",
+ "parameters": [
+ {
+ "name": "bridgeId",
+ "description": "Bridge's id",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "variable",
+ "description": "The bridge variable or function to set",
+ "paramType": "query",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "value",
+ "description": "The value to set the variable to",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "report_events",
+ "description": "Whether this variable should be included in bridge events. Defaults to false.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "boolean",
+ "defaultValue": false
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 400,
+ "reason": "Missing variable parameter."
+ },
+ {
+ "code": 404,
+ "reason": "Bridge not found"
+ },
+ {
+ "code": 409,
+ "reason": "Bridge not in a Stasis application"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "path": "/bridges/{bridgeId}/variables",
+ "description": "Multiple variables on a bridge",
+ "operations": [
+ {
+ "httpMethod": "GET",
+ "since": [
+ "20.20.0",
+ "22.10.0",
+ "23.4.0"
+ ],
+ "summary": "Get the value of multiple bridge variables or functions.",
+ "nickname": "getBridgeVars",
+ "responseClass": "Variables",
+ "parameters": [
+ {
+ "name": "bridgeId",
+ "description": "Bridge's id",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "variables",
+ "description": "The bridge variables or functions to get",
+ "paramType": "query",
+ "required": true,
+ "allowMultiple": true,
+ "dataType": "string"
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 400,
+ "reason": "Missing variables parameter."
+ },
+ {
+ "code": 404,
+ "reason": "Bridge or variable not found"
+ },
+ {
+ "code": 409,
+ "reason": "Bridge not in a Stasis application"
+ }
+ ]
+ },
+ {
+ "httpMethod": "POST",
+ "since": [
+ "20.20.0",
+ "22.10.0",
+ "23.4.0"
+ ],
+ "summary": "Set the values of multiple bridge variables or functions.",
+ "nickname": "setBridgeVars",
+ "responseClass": "void",
+ "parameters": [
+ {
+ "name": "bridgeId",
+ "description": "Bridge's id",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "variables",
+ "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the bridge. Each variable value may be either a string or an object containing \"value\" (string) and optional \"report_events\" (boolean) to include updates for that variable in bridge events (defaults to false). Ex. { \"variables\": { \"Bridge_State\": \"WaitingForAgent\", \"Support_Level\": { \"value\": \"Premium\", \"report_events\": true } } }",
+ "paramType": "body",
+ "required": true,
+ "dataType": "containers",
+ "allowMultiple": false
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 400,
+ "reason": "Missing variables parameter."
+ },
+ {
+ "code": 404,
+ "reason": "Bridge not found"
+ },
+ {
+ "code": 409,
+ "reason": "Bridge not in a Stasis application"
+ }
+ ]
+ }
+ ]
+ },
{
"path": "/bridges/{bridgeId}/addChannel",
"description": "Add a channel to a bridge",
"required": true,
"type": "Date",
"description": "Timestamp when bridge was created"
+ },
+ "bridgevars": {
+ "required": false,
+ "type": "object",
+ "description": "Bridge variables"
}
}
}
},
{
"name": "variables",
- "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { \"endpoint\": \"SIP/Alice\", \"variables\": { \"CALLERID(name)\": \"Alice\" } }",
+ "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel on creation. Each variable value may be either a string or an object containing \"value\" (string) and optional \"report_events\" (boolean) to include updates for that variable in channel events (defaults to false). Other keys in the body object are interpreted as query parameters. Ex. { \"endpoint\": \"SIP/Alice\", \"variables\": { \"CALLERID(name)\": \"Alice\", \"Call_State\": { \"value\": \"WaitingForAgent\", \"report_events\": true } } }",
"paramType": "body",
"required": false,
"dataType": "containers",
},
{
"name": "variables",
- "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { \"endpoint\": \"SIP/Alice\", \"variables\": { \"CALLERID(name)\": \"Alice\" } }",
+ "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel on creation. Each variable value may be either a string or an object containing \"value\" (string) and optional \"report_events\" (boolean) to include updates for that variable in channel events (defaults to false). Other keys in the body object are interpreted as query parameters. Ex. { \"endpoint\": \"SIP/Alice\", \"variables\": { \"CALLERID(name)\": \"Alice\", \"Call_State\": { \"value\": \"WaitingForAgent\", \"report_events\": true } } }",
"paramType": "body",
"required": false,
"dataType": "containers",
},
{
"name": "variables",
- "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { \"endpoint\": \"SIP/Alice\", \"variables\": { \"CALLERID(name)\": \"Alice\" } }",
+ "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel on creation. Each variable value may be either a string or an object containing \"value\" (string) and optional \"report_events\" (boolean) to include updates for that variable in channel events (defaults to false). Other keys in the body object are interpreted as query parameters. Ex. { \"endpoint\": \"SIP/Alice\", \"variables\": { \"CALLERID(name)\": \"Alice\", \"Call_State\": { \"value\": \"WaitingForAgent\", \"report_events\": true } } }",
"paramType": "body",
"required": false,
"dataType": "containers",
"required": false,
"allowMultiple": false,
"dataType": "string"
+ },
+ {
+ "name": "report_events",
+ "description": "Whether this variable should be included in channel events. Defaults to false.",
+ "paramType": "query",
+ "required": false,
+ "allowMultiple": false,
+ "dataType": "boolean",
+ "defaultValue": false
}
],
"errorResponses": [
},
{
"name": "variables",
- "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel. Ex. { \"variables\": { \"CALLERID(name)\": \"Alice\" } }",
+ "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel. Each variable value may be either a string or an object containing \"value\" (string) and optional \"report_events\" (boolean) to include updates for that variable in channel events (defaults to false). Ex. { \"variables\": { \"CALLERID(name)\": \"Alice\", \"Call_State\": { \"value\": \"WaitingForAgent\", \"report_events\": true } } }",
"paramType": "body",
- "required": false,
+ "required": true,
"dataType": "containers",
"allowMultiple": false
}
},
{
"name": "variables",
- "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { \"endpoint\": \"SIP/Alice\", \"variables\": { \"CALLERID(name)\": \"Alice\" } }",
+ "description": "The \"variables\" key in the body object holds variable key/value pairs to set on the channel on creation. Each variable value may be either a string or an object containing \"value\" (string) and optional \"report_events\" (boolean) to include updates for that variable in channel events (defaults to false). Other keys in the body object are interpreted as query parameters. Ex. { \"endpoint\": \"SIP/Alice\", \"variables\": { \"CALLERID(name)\": \"Alice\", \"Call_State\": { \"value\": \"WaitingForAgent\", \"report_events\": true } } }",
"paramType": "body",
"required": false,
"dataType": "containers",