[svn-commits] kmoore: branch group/ari-greedy-atxfer r419315 - in /team/group/ari-greedy-at...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Wed Jul 23 10:59:22 CDT 2014


Author: kmoore
Date: Wed Jul 23 10:59:15 2014
New Revision: 419315

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=419315
Log:
Stasis: Prevent non-stasis channels from entering stasis bridges

This intercepts channels attempting to enter stasis-controlled bridges
that are not themselves controlled by stasis and routes them through
Stasis() to be controlled by the Stasis application that controls the
channels they are replacing.

ASTERISK-23941
Review: https://reviewboard.asterisk.org/r/3731/

Modified:
    team/group/ari-greedy-atxfer/res/ari/ari_model_validators.c
    team/group/ari-greedy-atxfer/res/ari/ari_model_validators.h
    team/group/ari-greedy-atxfer/res/res_stasis.c
    team/group/ari-greedy-atxfer/res/stasis/app.c
    team/group/ari-greedy-atxfer/res/stasis/app.h
    team/group/ari-greedy-atxfer/res/stasis/control.c
    team/group/ari-greedy-atxfer/res/stasis/control.h
    team/group/ari-greedy-atxfer/res/stasis/stasis_bridge.c
    team/group/ari-greedy-atxfer/rest-api/api-docs/events.json

Modified: team/group/ari-greedy-atxfer/res/ari/ari_model_validators.c
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/res/ari/ari_model_validators.c?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/res/ari/ari_model_validators.c (original)
+++ team/group/ari-greedy-atxfer/res/ari/ari_model_validators.c Wed Jul 23 10:59:15 2014
@@ -4670,6 +4670,15 @@
 				res = 0;
 			}
 		} else
+		if (strcmp("replace_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 StasisStart field replace_channel failed validation\n");
+				res = 0;
+			}
+		} else
 		{
 			ast_log(LOG_ERROR,
 				"ARI StasisStart has undocumented field %s\n",

Modified: team/group/ari-greedy-atxfer/res/ari/ari_model_validators.h
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/res/ari/ari_model_validators.h?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/res/ari/ari_model_validators.h (original)
+++ team/group/ari-greedy-atxfer/res/ari/ari_model_validators.h Wed Jul 23 10:59:15 2014
@@ -1403,6 +1403,7 @@
  * - timestamp: Date
  * - args: List[string] (required)
  * - channel: Channel (required)
+ * - replace_channel: Channel
  * Application
  * - bridge_ids: List[string] (required)
  * - channel_ids: List[string] (required)

Modified: team/group/ari-greedy-atxfer/res/res_stasis.c
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/res/res_stasis.c?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/res/res_stasis.c (original)
+++ team/group/ari-greedy-atxfer/res/res_stasis.c Wed Jul 23 10:59:15 2014
@@ -725,6 +725,112 @@
 	ast_bridge_destroy(bridge, 0);
 }
 
+struct replace_channel_store {
+	struct ast_channel_snapshot *snapshot;
+	char *app;
+};
+
+static void replace_channel_destroy(void *obj)
+{
+	struct replace_channel_store *replace = obj;
+
+	ao2_cleanup(replace->snapshot);
+	ast_free(replace->app);
+	ast_free(replace);
+}
+
+static const struct ast_datastore_info replace_channel_store_info = {
+	.type = "replace-channel-store",
+	.destroy = replace_channel_destroy,
+};
+
+static struct replace_channel_store *get_replace_channel_store(struct ast_channel *chan, int no_create)
+{
+	struct ast_datastore *datastore;
+
+	SCOPED_CHANNELLOCK(lock, chan);
+	datastore = ast_channel_datastore_find(chan, &replace_channel_store_info, NULL);
+	if (!datastore) {
+		if (no_create) {
+			return NULL;
+		}
+
+		datastore = ast_datastore_alloc(&replace_channel_store_info, NULL);
+		if (!datastore) {
+			return NULL;
+		}
+		ast_channel_datastore_add(chan, datastore);
+	}
+
+	if (!datastore->data) {
+		datastore->data = ast_calloc(1, sizeof(struct replace_channel_store));
+	}
+	return datastore->data;
+}
+
+int app_set_replace_channel_snapshot(struct ast_channel *chan, struct ast_channel_snapshot *replace_snapshot)
+{
+	struct replace_channel_store *replace = get_replace_channel_store(chan, 0);
+
+	if (!replace) {
+		return -1;
+	}
+
+	ao2_replace(replace->snapshot, replace_snapshot);
+	return 0;
+}
+
+int app_set_replace_channel_app(struct ast_channel *chan, const char *replace_app)
+{
+	struct replace_channel_store *replace = get_replace_channel_store(chan, 0);
+
+	if (!replace) {
+		return -1;
+	}
+
+	ast_free(replace->app);
+	replace->app = NULL;
+
+	if (replace_app) {
+		replace->app = ast_strdup(replace_app);
+		if (!replace->app) {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static struct ast_channel_snapshot *get_replace_channel_snapshot(struct ast_channel *chan)
+{
+	struct replace_channel_store *replace = get_replace_channel_store(chan, 1);
+	struct ast_channel_snapshot *replace_channel_snapshot;
+
+	if (!replace) {
+		return NULL;
+	}
+
+	replace_channel_snapshot = replace->snapshot;
+	replace->snapshot = NULL;
+
+	return replace_channel_snapshot;
+}
+
+char *app_get_replace_channel_app(struct ast_channel *chan)
+{
+	struct replace_channel_store *replace = get_replace_channel_store(chan, 1);
+	char *replace_channel_app;
+
+	if (!replace) {
+		return NULL;
+	}
+
+	replace_channel_app = replace->app;
+	replace->app = NULL;
+
+	return replace_channel_app;
+}
+
 static int send_start_msg(struct stasis_app *app, struct ast_channel *chan,
 	int argc, char *argv[])
 {
@@ -734,8 +840,12 @@
 	struct ast_json *json_args;
 	int i;
 	struct stasis_message_sanitizer *sanitize = stasis_app_get_sanitizer();
+	RAII_VAR(struct ast_channel_snapshot *, replace_channel_snapshot,
+		NULL, ao2_cleanup);
 
 	ast_assert(chan != NULL);
+
+	replace_channel_snapshot = get_replace_channel_snapshot(chan);
 
 	/* Set channel info */
 	ast_channel_lock(chan);
@@ -757,6 +867,15 @@
 		"channel", ast_channel_snapshot_to_json(snapshot, NULL));
 	if (!msg) {
 		return -1;
+	}
+
+	if (replace_channel_snapshot) {
+		int res = ast_json_object_set(msg, "replace_channel",
+			ast_channel_snapshot_to_json(replace_channel_snapshot, NULL));
+
+		if (res) {
+			return -1;
+		}
 	}
 
 	/* Append arguments to args array */

Modified: team/group/ari-greedy-atxfer/res/stasis/app.c
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/res/stasis/app.c?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/res/stasis/app.c (original)
+++ team/group/ari-greedy-atxfer/res/stasis/app.c Wed Jul 23 10:59:15 2014
@@ -28,6 +28,7 @@
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "app.h"
+#include "control.h"
 
 #include "asterisk/callerid.h"
 #include "asterisk/stasis_app.h"
@@ -712,6 +713,21 @@
 	if (subscribed) {
 		stasis_publish(app->topic, message);
 	}
+
+	if (transfer_msg->replace_channel) {
+		struct stasis_app_control *control = stasis_app_control_find_by_channel_id(
+			transfer_msg->to_transferee.channel_snapshot->uniqueid);
+		struct ast_channel *chan = ast_channel_get_by_name(transfer_msg->replace_channel->uniqueid);
+
+		if (control && chan) {
+			ast_channel_lock(chan);
+			app_set_replace_channel_app(chan, app_name(control_app(control)));
+			app_set_replace_channel_snapshot(chan, transfer_msg->to_transferee.channel_snapshot);
+			ast_channel_unlock(chan);
+		}
+		ast_channel_cleanup(chan);
+		ao2_cleanup(control);
+	}
 }
 
 static void bridge_default_handler(void *data, struct stasis_subscription *sub,

Modified: team/group/ari-greedy-atxfer/res/stasis/app.h
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/res/stasis/app.h?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/res/stasis/app.h (original)
+++ team/group/ari-greedy-atxfer/res/stasis/app.h Wed Jul 23 10:59:15 2014
@@ -226,4 +226,36 @@
  */
 int app_is_subscribed_endpoint_id(struct stasis_app *app, const char *endpoint_id);
 
+/*!
+ * \brief Set the snapshot of the channel that this channel will replace
+ *
+ * \param channel The channel on which this will be set
+ * \param replace_snapshot The snapshot of the channel that is being replaced
+ *
+ * \retval zero success
+ * \retval non-zero failure
+ */
+int app_set_replace_channel_snapshot(struct ast_channel *chan, struct ast_channel_snapshot *replace_snapshot);
+
+/*!
+ * \brief Set the app that the replacement channel will be controlled by
+ *
+ * \param channel The channel on which this will be set
+ * \param replace_app The app that will be controlling this channel
+ *
+ * \retval zero success
+ * \retval non-zero failure
+ */
+int app_set_replace_channel_app(struct ast_channel *chan, const char *replace_app);
+
+/*!
+ * \brief Get the app that the replacement channel will be controlled by
+ *
+ * \param channel The channel on which this will be set
+ *
+ * \retval NULL on error
+ * \return the name of the controlling app (must be ast_free()d)
+ */
+char *app_get_replace_channel_app(struct ast_channel *chan);
+
 #endif /* _ASTERISK_RES_STASIS_APP_H */

Modified: team/group/ari-greedy-atxfer/res/stasis/control.c
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/res/stasis/control.c?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/res/stasis/control.c (original)
+++ team/group/ari-greedy-atxfer/res/stasis/control.c Wed Jul 23 10:59:15 2014
@@ -276,10 +276,6 @@
 	int timeout;
 };
 
-static int app_control_add_channel_to_bridge(
-        struct stasis_app_control *control,
-        struct ast_channel *chan, void *data);
-
 static int app_control_dial(struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
@@ -322,7 +318,7 @@
 		AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
 		ast_hangup(new_chan);
 	} else {
-		app_control_add_channel_to_bridge(control, chan, bridge);
+		control_add_channel_to_bridge(control, chan, bridge);
 	}
 
 	return 0;
@@ -855,7 +851,7 @@
 		ast_bridge_after_cb_reason_string(reason));
 }
 
-static int app_control_add_channel_to_bridge(
+int control_add_channel_to_bridge(
 	struct stasis_app_control *control,
 	struct ast_channel *chan, void *data)
 {
@@ -935,7 +931,7 @@
 			stasis_app_control_get_channel_id(control));
 
 	return app_send_command_on_condition(
-		control, app_control_add_channel_to_bridge, bridge,
+		control, control_add_channel_to_bridge, bridge,
 		app_control_can_add_channel_to_bridge);
 }
 
@@ -1064,3 +1060,8 @@
 	ao2_cleanup(command_queue);
 	return count;
 }
+
+struct stasis_app *control_app(struct stasis_app_control *control)
+{
+	return control->app;
+}

Modified: team/group/ari-greedy-atxfer/res/stasis/control.h
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/res/stasis/control.h?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/res/stasis/control.h (original)
+++ team/group/ari-greedy-atxfer/res/stasis/control.h Wed Jul 23 10:59:15 2014
@@ -88,5 +88,25 @@
 int control_prestart_dispatch_all(struct stasis_app_control *control,
 	struct ast_channel *chan);
 
+/*!
+ * \brief Returns the pointer (non-reffed) to the app associated with this control
+ *
+ * \param control Control to query.
+ *
+ * \returns A pointer to the associated stasis_app
+ */
+struct stasis_app *control_app(struct stasis_app_control *control);
+
+/*!
+ * \brief Command callback for adding a channel to a bridge
+ *
+ * \param control The control for chan
+ * \param channel The channel on which commands should be executed
+ * \param bridge Data to be passed to the callback
+ */
+int control_add_channel_to_bridge(
+	struct stasis_app_control *control,
+	struct ast_channel *chan, void *obj);
+
 
 #endif /* _ASTERISK_RES_STASIS_CONTROL_H */

Modified: team/group/ari-greedy-atxfer/res/stasis/stasis_bridge.c
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/res/stasis/stasis_bridge.c?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/res/stasis/stasis_bridge.c (original)
+++ team/group/ari-greedy-atxfer/res/stasis/stasis_bridge.c Wed Jul 23 10:59:15 2014
@@ -32,10 +32,68 @@
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/bridge.h"
+#include "asterisk/bridge_after.h"
 #include "asterisk/bridge_internal.h"
 #include "stasis_bridge.h"
+#include "control.h"
+#include "command.h"
+#include "app.h"
+#include "asterisk/stasis_app.h"
+#include "asterisk/pbx.h"
 
 /* ------------------------------------------------------------------- */
+
+static void bridge_stasis_run_cb(struct ast_channel *chan, void *data)
+{
+	RAII_VAR(char *, app_name, data, ast_free);
+	struct ast_app *app_stasis;
+
+
+	/* find Stasis() */
+	app_stasis = pbx_findapp("Stasis");
+	if (!app_stasis) {
+		ast_log(LOG_WARNING, "Could not find application (Stasis)\n");
+		return;
+	}
+
+	if (ast_check_hangup_locked(chan)) {
+		/* channel hungup, don't run Stasis() */
+		return;
+	}
+
+	/* run Stasis() */
+	pbx_exec(chan, app_stasis, app_name);
+}
+
+static void bridge_stasis_fail_cb(enum ast_bridge_after_cb_reason reason, void *data)
+{
+	char *app_name = data;
+
+	ast_free(app_name);
+}
+
+static int add_channel_to_bridge(
+	struct stasis_app_control *control,
+	struct ast_channel *chan, void *obj)
+{
+	struct ast_bridge *bridge = obj;
+	int res;
+
+	res = control_add_channel_to_bridge(control,
+		chan, bridge);
+	ao2_cleanup(bridge);
+	return res;
+}
+
+static void bridge_stasis_queue_join_action(struct ast_bridge *self,
+	struct ast_bridge_channel *bridge_channel)
+{
+	ast_channel_lock(bridge_channel->chan);
+	if (command_prestart_queue_command(bridge_channel->chan, add_channel_to_bridge, ao2_bump(self))) {
+		ao2_cleanup(self);
+	}
+	ast_channel_unlock(bridge_channel->chan);
+}
 
 /*!
  * \internal
@@ -53,6 +111,34 @@
  */
 static int bridge_stasis_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
 {
+	struct stasis_app_control *control = stasis_app_control_find_by_channel(bridge_channel->chan);
+
+	if (!control) {
+		/* channel not in Stasis(), get it there */
+		char *swap_app;
+
+		/* Take ownership of the swap_app memory from the datastore */
+		swap_app = app_get_replace_channel_app(bridge_channel->chan);
+		if (!swap_app) {
+			ast_log(LOG_ERROR, "Failed to get app name for channel at %p\n", bridge_channel->chan);
+			return -1;
+		}
+
+		/* Attach after-bridge callback and pass ownership of swap_app to it */
+		if (ast_bridge_set_after_callback(bridge_channel->chan,
+			bridge_stasis_run_cb, bridge_stasis_fail_cb, swap_app)) {
+			ast_log(LOG_ERROR, "Failed to set after bridge callback\n");
+			ast_free(swap_app);
+			return -1;
+		}
+
+		bridge_stasis_queue_join_action(self, bridge_channel);
+
+		/* Return -1 so the push fails and the after-bridge callback gets called */
+		return -1;
+	}
+
+	ao2_cleanup(control);
 	if (self->allowed_capabilities & STASIS_BRIDGE_MIXING_CAPABILITIES) {
 		ast_bridge_channel_update_linkedids(bridge_channel, swap);
 		if (ast_test_flag(&self->feature_flags, AST_BRIDGE_FLAG_SMART)) {

Modified: team/group/ari-greedy-atxfer/rest-api/api-docs/events.json
URL: http://svnview.digium.com/svn/asterisk/team/group/ari-greedy-atxfer/rest-api/api-docs/events.json?view=diff&rev=419315&r1=419314&r2=419315
==============================================================================
--- team/group/ari-greedy-atxfer/rest-api/api-docs/events.json (original)
+++ team/group/ari-greedy-atxfer/rest-api/api-docs/events.json Wed Jul 23 10:59:15 2014
@@ -674,6 +674,10 @@
 				"channel": {
 					"required": true,
 					"type": "Channel"
+				},
+				"replace_channel": {
+					"required": false,
+					"type": "Channel"
 				}
 			}
 		}




More information about the svn-commits mailing list