summaryrefslogtreecommitdiff
path: root/res/stasis
diff options
context:
space:
mode:
Diffstat (limited to 'res/stasis')
-rw-r--r--res/stasis/app.c61
-rw-r--r--res/stasis/app.h44
-rw-r--r--res/stasis/command.c58
-rw-r--r--res/stasis/command.h27
-rw-r--r--res/stasis/control.c43
-rw-r--r--res/stasis/control.h31
-rw-r--r--res/stasis/stasis_bridge.c111
7 files changed, 363 insertions, 12 deletions
diff --git a/res/stasis/app.c b/res/stasis/app.c
index 7e7911b9c..745969615 100644
--- a/res/stasis/app.c
+++ b/res/stasis/app.c
@@ -28,6 +28,7 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "app.h"
+#include "control.h"
#include "messaging.h"
#include "asterisk/callerid.h"
@@ -699,14 +700,32 @@ static void bridge_blind_transfer_handler(void *data, struct stasis_subscription
struct stasis_message *message)
{
struct stasis_app *app = data;
- struct ast_bridge_blob *blob = stasis_message_data(message);
+ struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
+ struct ast_bridge_snapshot *bridge = transfer_msg->to_transferee.bridge_snapshot;
- if (bridge_app_subscribed(app, blob->channel->uniqueid) ||
- (blob->bridge && bridge_app_subscribed_involved(app, blob->bridge))) {
+ if (bridge_app_subscribed(app, transfer_msg->to_transferee.channel_snapshot->uniqueid) ||
+ (bridge && bridge_app_subscribed_involved(app, bridge))) {
stasis_publish(app->topic, message);
}
}
+static void set_replacement_channel(struct ast_channel_snapshot *to_be_replaced,
+ struct ast_channel_snapshot *replacing)
+{
+ struct stasis_app_control *control = stasis_app_control_find_by_channel_id(
+ to_be_replaced->uniqueid);
+ struct ast_channel *chan = ast_channel_get_by_name(replacing->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, to_be_replaced);
+ ast_channel_unlock(chan);
+ }
+ ast_channel_cleanup(chan);
+ ao2_cleanup(control);
+}
+
static void bridge_attended_transfer_handler(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
@@ -751,6 +770,18 @@ static void bridge_attended_transfer_handler(void *data, struct stasis_subscript
if (subscribed) {
stasis_publish(app->topic, message);
}
+
+ if (transfer_msg->replace_channel) {
+ set_replacement_channel(transfer_msg->to_transferee.channel_snapshot,
+ transfer_msg->replace_channel);
+ }
+
+ if (transfer_msg->dest_type == AST_ATTENDED_TRANSFER_DEST_LINK) {
+ set_replacement_channel(transfer_msg->to_transferee.channel_snapshot,
+ transfer_msg->dest.links[0]);
+ set_replacement_channel(transfer_msg->to_transfer_target.channel_snapshot,
+ transfer_msg->dest.links[1]);
+ }
}
static void bridge_default_handler(void *data, struct stasis_subscription *sub,
@@ -1091,6 +1122,30 @@ int app_is_subscribed_channel_id(struct stasis_app *app, const char *channel_id)
return forwards != NULL;
}
+int app_replace_channel_forwards(struct stasis_app *app, const char *old_id, struct ast_channel *new_chan)
+{
+ RAII_VAR(struct app_forwards *, old_forwards, NULL, ao2_cleanup);
+ struct app_forwards *new_forwards;
+
+ old_forwards = ao2_find(app->forwards, old_id, OBJ_SEARCH_KEY | OBJ_UNLINK);
+ if (!old_forwards) {
+ return -1;
+ }
+
+ new_forwards = forwards_create_channel(app, new_chan);
+ if (!new_forwards) {
+ return -1;
+ }
+
+ new_forwards->interested = old_forwards->interested;
+ ao2_link_flags(app->forwards, new_forwards, 0);
+ ao2_cleanup(new_forwards);
+
+ /* Clean up old forwards */
+ forwards_unsubscribe(old_forwards);
+ return 0;
+}
+
static void *channel_find(const struct stasis_app *app, const char *id)
{
return ast_channel_get_by_name(id);
diff --git a/res/stasis/app.h b/res/stasis/app.h
index 419ec54a8..1ab6097a7 100644
--- a/res/stasis/app.h
+++ b/res/stasis/app.h
@@ -226,4 +226,48 @@ int app_unsubscribe_endpoint_id(struct stasis_app *app, const char *endpoint_id)
*/
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);
+
+/*!
+ * \brief Replace channel topic forwards for the old channel with forwards for the new channel
+ *
+ * \param app The app that owns the channel
+ * \param old_id The unique ID of the channel to be replaced
+ * \param new_chan The channel that is replacing the old one
+ *
+ * \retval zero on success
+ * \return non-zero on failure
+ */
+int app_replace_channel_forwards(struct stasis_app *app, const char *old_id, struct ast_channel *new_chan);
+
#endif /* _ASTERISK_RES_STASIS_APP_H */
diff --git a/res/stasis/command.c b/res/stasis/command.c
index a9e53af12..867de180a 100644
--- a/res/stasis/command.c
+++ b/res/stasis/command.c
@@ -93,3 +93,61 @@ void command_invoke(struct stasis_app_command *command,
command_complete(command, retval);
}
+static void command_queue_prestart_destroy(void *obj)
+{
+ /* Clean up the container */
+ ao2_cleanup(obj);
+}
+
+static const struct ast_datastore_info command_queue_prestart = {
+ .type = "stasis-command-prestart-queue",
+ .destroy = command_queue_prestart_destroy,
+};
+
+int command_prestart_queue_command(struct ast_channel *chan,
+ stasis_app_command_cb command_fn, void *data)
+{
+ struct ast_datastore *datastore;
+ struct ao2_container *command_queue;
+ RAII_VAR(struct stasis_app_command *, command,
+ command_create(command_fn, data), ao2_cleanup);
+
+ if (!command) {
+ return -1;
+ }
+
+ datastore = ast_channel_datastore_find(chan, &command_queue_prestart, NULL);
+ if (datastore) {
+ command_queue = datastore->data;
+ ao2_link(command_queue, command);
+ return 0;
+ }
+
+ command_queue = ao2_container_alloc_list(
+ AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
+ if (!command_queue) {
+ return -1;
+ }
+
+ datastore = ast_datastore_alloc(&command_queue_prestart, NULL);
+ if (!datastore) {
+ ao2_cleanup(command_queue);
+ return -1;
+ }
+ ast_channel_datastore_add(chan, datastore);
+
+ datastore->data = command_queue;
+ ao2_link(command_queue, command);
+
+ return 0;
+}
+
+struct ao2_container *command_prestart_get_container(struct ast_channel *chan)
+{
+ struct ast_datastore *datastore = ast_channel_datastore_find(chan, &command_queue_prestart, NULL);
+ if (!datastore) {
+ return NULL;
+ }
+
+ return ao2_bump(datastore->data);
+}
diff --git a/res/stasis/command.h b/res/stasis/command.h
index a99d40d0a..7f12ab36f 100644
--- a/res/stasis/command.h
+++ b/res/stasis/command.h
@@ -41,4 +41,31 @@ void command_invoke(struct stasis_app_command *command,
int command_join(struct stasis_app_command *command);
+/*!
+ * \brief Queue a Stasis() prestart command for a channel
+ *
+ * \pre chan must be locked
+ *
+ * \param chan The channel on which to queue the prestart command
+ * \param command_fn The callback to call for the command
+ * \param data The data to pass to the command callback
+ *
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int command_prestart_queue_command(struct ast_channel *chan,
+ stasis_app_command_cb command_fn, void *data);
+
+/*!
+ * \brief Get the Stasis() prestart commands for a channel
+ *
+ * \pre chan must be locked
+ *
+ * \param chan The channel from which to get prestart commands
+ *
+ * \return The command prestart container for chan (must be ao2_cleanup()'d)
+ */
+struct ao2_container *command_prestart_get_container(struct ast_channel *chan);
+
+
#endif /* _ASTERISK_RES_STASIS_CONTROL_H */
diff --git a/res/stasis/control.c b/res/stasis/control.c
index 8802e8128..0a9669d3b 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -276,10 +276,6 @@ struct stasis_app_control_dial_data {
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 @@ static int app_control_dial(struct stasis_app_control *control,
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 @@ static void bridge_after_cb_failed(enum ast_bridge_after_cb_reason reason,
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 @@ int stasis_app_control_add_channel_to_bridge(
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);
}
@@ -1036,3 +1032,36 @@ void control_wait(struct stasis_app_control *control)
}
ao2_unlock(control->command_queue);
}
+
+int control_prestart_dispatch_all(struct stasis_app_control *control,
+ struct ast_channel *chan)
+{
+ struct ao2_container *command_queue;
+ int count = 0;
+ struct ao2_iterator iter;
+ struct stasis_app_command *command;
+
+ ast_channel_lock(chan);
+ command_queue = command_prestart_get_container(chan);
+ ast_channel_unlock(chan);
+ if (!command_queue) {
+ return 0;
+ }
+
+ iter = ao2_iterator_init(command_queue, AO2_ITERATOR_UNLINK);
+
+ while ((command = ao2_iterator_next(&iter))) {
+ command_invoke(command, control, chan);
+ ao2_cleanup(command);
+ ++count;
+ }
+
+ ao2_iterator_destroy(&iter);
+ ao2_cleanup(command_queue);
+ return count;
+}
+
+struct stasis_app *control_app(struct stasis_app_control *control)
+{
+ return control->app;
+}
diff --git a/res/stasis/control.h b/res/stasis/control.h
index 0febd8438..a139f82e4 100644
--- a/res/stasis/control.h
+++ b/res/stasis/control.h
@@ -77,5 +77,36 @@ int control_is_done(struct stasis_app_control *control);
void control_mark_done(struct stasis_app_control *control);
+/*!
+ * \brief Dispatch all queued prestart commands
+ *
+ * \param control The control for chan
+ * \param channel The channel on which commands should be executed
+ *
+ * \return The number of commands executed
+ */
+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 */
diff --git a/res/stasis/stasis_bridge.c b/res/stasis/stasis_bridge.c
index c3a266a11..be7836d35 100644
--- a/res/stasis/stasis_bridge.c
+++ b/res/stasis/stasis_bridge.c
@@ -32,11 +32,73 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/bridge.h"
+#include "asterisk/bridge_after.h"
#include "asterisk/bridge_internal.h"
+#include "asterisk/bridge_features.h"
+#include "asterisk/stasis_app.h"
+#include "asterisk/stasis_channels.h"
#include "stasis_bridge.h"
+#include "control.h"
+#include "command.h"
+#include "app.h"
+#include "asterisk/stasis_app.h"
+#include "asterisk/pbx.h"
/* ------------------------------------------------------------------- */
+static struct ast_bridge_methods bridge_stasis_v_table;
+
+static void bridge_stasis_run_cb(struct ast_channel *chan, void *data)
+{
+ RAII_VAR(char *, app_name, NULL, ast_free);
+ struct ast_app *app_stasis;
+
+ /* Take ownership of the swap_app memory from the datastore */
+ app_name = app_get_replace_channel_app(chan);
+ if (!app_name) {
+ ast_log(LOG_ERROR, "Failed to get app name for %s (%p)\n", ast_channel_name(chan), chan);
+ return;
+ }
+
+ /* 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 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
* \brief Push this channel into the Stasis bridge.
@@ -53,6 +115,24 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
*/
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 */
+ /* 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, NULL, NULL)) {
+ ast_log(LOG_ERROR, "Failed to set after bridge callback\n");
+ 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)) {
@@ -63,6 +143,33 @@ static int bridge_stasis_push(struct ast_bridge *self, struct ast_bridge_channel
return ast_bridge_base_v_table.push(self, bridge_channel, swap);
}
+static int bridge_stasis_moving(struct ast_bridge_channel *bridge_channel, void *hook_pvt,
+ struct ast_bridge *src, struct ast_bridge *dst)
+{
+ if (src->v_table == &bridge_stasis_v_table &&
+ dst->v_table != &bridge_stasis_v_table) {
+ RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+ RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+ struct ast_channel *chan;
+
+ chan = bridge_channel->chan;
+ ast_assert(chan != NULL);
+
+ control = stasis_app_control_find_by_channel(chan);
+ if (!control) {
+ return -1;
+ }
+
+ blob = ast_json_pack("{s: s}", "app", app_name(control_app(control)));
+
+ stasis_app_channel_set_stasis_end_published(chan);
+
+ ast_channel_publish_blob(chan, ast_stasis_end_message_type(), blob);
+ }
+
+ return -1;
+}
+
/*!
* \internal
* \brief Pull this channel from the Stasis bridge.
@@ -82,11 +189,11 @@ static void bridge_stasis_pull(struct ast_bridge *self, struct ast_bridge_channe
ast_bridge_channel_update_accountcodes(NULL, bridge_channel);
}
+ ast_bridge_move_hook(bridge_channel->features, bridge_stasis_moving, NULL, NULL, 0);
+
ast_bridge_base_v_table.pull(self, bridge_channel);
}
-static struct ast_bridge_methods bridge_stasis_v_table;
-
struct ast_bridge *bridge_stasis_new(uint32_t capabilities, unsigned int flags, const char *name, const char *id)
{
void *bridge;