summaryrefslogtreecommitdiff
path: root/main/bridge.c
diff options
context:
space:
mode:
authorKinsey Moore <kmoore@digium.com>2014-08-07 15:30:19 +0000
committerKinsey Moore <kmoore@digium.com>2014-08-07 15:30:19 +0000
commit0ac7f96057fb9fc0d012515f47bfea8d63eb5199 (patch)
tree8f807ace812c5d8d1cccb01b021ae050e975e550 /main/bridge.c
parenta8829490b6b9891b352e39f3846de1f274ca632c (diff)
Stasis: Convey transfer information to applications
This fixes a class of issues where Stasis applications were not made aware that their channels were being manipulated or replaced by external entitiessuch as transfers, AMI commands, or dialplan applications such as Bridge(). Inconsistent information such as StasisEnd events with unknown channels as a result of masquerades has also been corrected. To accomplish these fixes, several new fields were added to blind and attended transfer messages as well as StasisStart and BridgeAttendedTransfer Stasis events. ASTERISK-23941 #close Review: https://reviewboard.asterisk.org/r/3865/ Review: https://reviewboard.asterisk.org/r/3857/ Review: https://reviewboard.asterisk.org/r/3852/ Review: https://reviewboard.asterisk.org/r/3816/ Review: https://reviewboard.asterisk.org/r/3731/ Review: https://reviewboard.asterisk.org/r/3729/ Review: https://reviewboard.asterisk.org/r/3728/ ........ Merged revisions 420325 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@420338 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/bridge.c')
-rw-r--r--main/bridge.c146
1 files changed, 107 insertions, 39 deletions
diff --git a/main/bridge.c b/main/bridge.c
index 462676ca8..926004bc9 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -1825,6 +1825,32 @@ static void bridge_channel_change_bridge(struct ast_bridge_channel *bridge_chann
ao2_ref(old_bridge, -1);
}
+static void bridge_channel_moving(struct ast_bridge_channel *bridge_channel, struct ast_bridge *src, struct ast_bridge *dst)
+{
+ struct ast_bridge_features *features = bridge_channel->features;
+ struct ast_bridge_hook *hook;
+ struct ao2_iterator iter;
+
+ /* Run any moving hooks. */
+ iter = ao2_iterator_init(features->other_hooks, 0);
+ for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
+ int remove_me;
+ ast_bridge_move_indicate_callback move_cb;
+
+ if (hook->type != AST_BRIDGE_HOOK_TYPE_MOVE) {
+ continue;
+ }
+ move_cb = (ast_bridge_move_indicate_callback) hook->callback;
+ remove_me = move_cb(bridge_channel, hook->hook_pvt, src, dst);
+ if (remove_me) {
+ ast_debug(1, "Move detection hook %p is being removed from %p(%s)\n",
+ hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+ ao2_unlink(features->other_hooks, hook);
+ }
+ }
+ ao2_iterator_destroy(&iter);
+}
+
void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
unsigned int optimized)
{
@@ -1873,6 +1899,8 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
continue;
}
+ bridge_channel_moving(bridge_channel, bridge_channel->bridge, dst_bridge);
+
/* Point to new bridge.*/
bridge_channel_change_bridge(bridge_channel, dst_bridge);
@@ -2122,6 +2150,8 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
ao2_ref(orig_bridge, +1);/* Keep a ref in case the push fails. */
bridge_channel_change_bridge(bridge_channel, dst_bridge);
+ bridge_channel_moving(bridge_channel, orig_bridge, dst_bridge);
+
if (bridge_channel_internal_push(bridge_channel)) {
/* Try to put the channel back into the original bridge. */
ast_bridge_features_remove(bridge_channel->features,
@@ -3089,6 +3119,18 @@ int ast_bridge_talk_detector_hook(struct ast_bridge_features *features,
AST_BRIDGE_HOOK_TYPE_TALK);
}
+int ast_bridge_move_hook(struct ast_bridge_features *features,
+ ast_bridge_move_indicate_callback callback,
+ void *hook_pvt,
+ ast_bridge_hook_pvt_destructor destructor,
+ enum ast_bridge_hook_remove_flags remove_flags)
+{
+ ast_bridge_hook_callback hook_cb = (ast_bridge_hook_callback) callback;
+
+ return bridge_other_hook(features, hook_cb, hook_pvt, destructor, remove_flags,
+ AST_BRIDGE_HOOK_TYPE_MOVE);
+}
+
int ast_bridge_interval_hook(struct ast_bridge_features *features,
enum ast_bridge_hook_timer_option flags,
unsigned int interval,
@@ -3828,14 +3870,55 @@ struct stasis_attended_transfer_publish_data {
struct ast_bridge_channel_pair to_transferee;
/* The bridge between the transferer and transfer target, and the transferer channel in this bridge */
struct ast_bridge_channel_pair to_transfer_target;
+ /* The Local;1 that will replace the transferee bridge transferer channel */
+ struct ast_channel *replace_channel;
+ /* The transferee channel. NULL if there is no transferee channel or if multiple parties are transferred */
+ struct ast_channel *transferee_channel;
+ /* The transfer target channel. NULL if there is no transfer target channel or if multiple parties are transferred */
+ struct ast_channel *target_channel;
};
+/*!
+ * \internal
+ * \brief Get the transferee channel
+ *
+ * This is only applicable to cases where a transfer is occurring on a
+ * two-party bridge. The channels container passed in is expected to only
+ * contain two channels, the transferer and the transferee. The transferer
+ * channel is passed in as a parameter to ensure we don't return it as
+ * the transferee channel.
+ *
+ * \param channels A two-channel container containing the transferer and transferee
+ * \param transferer The party that is transfering the call
+ * \return The party that is being transferred
+ */
+static struct ast_channel *get_transferee(struct ao2_container *channels, struct ast_channel *transferer)
+{
+ struct ao2_iterator channel_iter;
+ struct ast_channel *transferee;
+
+ for (channel_iter = ao2_iterator_init(channels, 0);
+ (transferee = ao2_iterator_next(&channel_iter));
+ ao2_cleanup(transferee)) {
+ if (transferee != transferer) {
+ break;
+ }
+ }
+
+ ao2_iterator_destroy(&channel_iter);
+ return transferee;
+}
+
+
static void stasis_publish_data_cleanup(struct stasis_attended_transfer_publish_data *publication)
{
ast_channel_unref(publication->to_transferee.channel);
ast_channel_unref(publication->to_transfer_target.channel);
+ ast_channel_cleanup(publication->transferee_channel);
+ ast_channel_cleanup(publication->target_channel);
ao2_cleanup(publication->to_transferee.bridge);
ao2_cleanup(publication->to_transfer_target.bridge);
+ ao2_cleanup(publication->replace_channel);
}
/*!
@@ -3865,6 +3948,9 @@ static void stasis_publish_data_init(struct ast_channel *to_transferee,
ao2_ref(to_target_bridge, +1);
publication->to_transfer_target.bridge = to_target_bridge;
}
+
+ publication->transferee_channel = ast_bridge_peer(to_transferee_bridge, to_transferee);
+ publication->target_channel = ast_bridge_peer(to_target_bridge, to_transfer_target);
}
/*
@@ -3878,7 +3964,8 @@ static void publish_attended_transfer_bridge_merge(struct stasis_attended_transf
struct ast_bridge *final_bridge)
{
ast_bridge_publish_attended_transfer_bridge_merge(1, AST_BRIDGE_TRANSFER_SUCCESS,
- &publication->to_transferee, &publication->to_transfer_target, final_bridge);
+ &publication->to_transferee, &publication->to_transfer_target, final_bridge,
+ publication->transferee_channel, publication->target_channel);
}
/*
@@ -3892,7 +3979,9 @@ static void publish_attended_transfer_app(struct stasis_attended_transfer_publis
const char *app)
{
ast_bridge_publish_attended_transfer_app(1, AST_BRIDGE_TRANSFER_SUCCESS,
- &publication->to_transferee, &publication->to_transfer_target, app);
+ &publication->to_transferee, &publication->to_transfer_target,
+ publication->replace_channel, app,
+ publication->transferee_channel, publication->target_channel);
}
/*
@@ -3909,7 +3998,8 @@ static void publish_attended_transfer_link(struct stasis_attended_transfer_publi
struct ast_channel *locals[2] = { local_channel1, local_channel2 };
ast_bridge_publish_attended_transfer_link(1, AST_BRIDGE_TRANSFER_SUCCESS,
- &publication->to_transferee, &publication->to_transfer_target, locals);
+ &publication->to_transferee, &publication->to_transfer_target, locals,
+ publication->transferee_channel, publication->target_channel);
}
/*
@@ -3923,7 +4013,8 @@ static void publish_attended_transfer_fail(struct stasis_attended_transfer_publi
enum ast_transfer_result result)
{
ast_bridge_publish_attended_transfer_fail(1, result, &publication->to_transferee,
- &publication->to_transfer_target);
+ &publication->to_transfer_target, publication->transferee_channel,
+ publication->target_channel);
}
/*!
@@ -3987,9 +4078,12 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
return AST_BRIDGE_TRANSFER_FAIL;
}
+ /* Get a ref for use later since this one is being stolen */
+ ao2_ref(local_chan, +1);
if (ast_bridge_impart(bridge1, local_chan, chan1, NULL,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
ast_hangup(local_chan);
+ ao2_cleanup(local_chan);
return AST_BRIDGE_TRANSFER_FAIL;
}
@@ -4005,40 +4099,12 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
publish_attended_transfer_link(publication,
local_chan, local_chan2);
} else {
+ publication->replace_channel = ao2_bump(local_chan);
publish_attended_transfer_app(publication, app);
}
- return AST_BRIDGE_TRANSFER_SUCCESS;
-}
-/*!
- * \internal
- * \brief Get the transferee channel
- *
- * This is only applicable to cases where a transfer is occurring on a
- * two-party bridge. The channels container passed in is expected to only
- * contain two channels, the transferer and the transferee. The transferer
- * channel is passed in as a parameter to ensure we don't return it as
- * the transferee channel.
- *
- * \param channels A two-channel container containing the transferer and transferee
- * \param transferer The party that is transfering the call
- * \return The party that is being transferred
- */
-static struct ast_channel *get_transferee(struct ao2_container *channels, struct ast_channel *transferer)
-{
- struct ao2_iterator channel_iter;
- struct ast_channel *transferee;
-
- for (channel_iter = ao2_iterator_init(channels, 0);
- (transferee = ao2_iterator_next(&channel_iter));
- ao2_cleanup(transferee)) {
- if (transferee != transferer) {
- break;
- }
- }
-
- ao2_iterator_destroy(&channel_iter);
- return transferee;
+ ao2_cleanup(local_chan);
+ return AST_BRIDGE_TRANSFER_SUCCESS;
}
static enum ast_transfer_result try_parking(struct ast_channel *transferer,
@@ -4142,7 +4208,7 @@ static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
static void publish_blind_transfer(int is_external, enum ast_transfer_result result,
struct ast_channel *transferer, struct ast_bridge *bridge,
- const char *context, const char *exten)
+ const char *context, const char *exten, struct ast_channel *transferee_channel)
{
struct ast_bridge_channel_pair pair;
pair.channel = transferer;
@@ -4150,7 +4216,7 @@ static void publish_blind_transfer(int is_external, enum ast_transfer_result res
if (bridge) {
ast_bridge_lock(bridge);
}
- ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten);
+ ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten, transferee_channel);
if (bridge) {
ast_bridge_unlock(bridge);
}
@@ -4174,6 +4240,9 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
}
+
+ transferee = ast_bridge_peer(bridge, transferer);
+
ast_channel_lock(transferer);
bridge_channel = ast_channel_get_bridge_channel(transferer);
ast_channel_unlock(transferer);
@@ -4235,7 +4304,6 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
/* Reaching this portion means that we're dealing with a two-party bridge */
- transferee = get_transferee(channels, transferer);
if (!transferee) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
@@ -4251,7 +4319,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
publish:
- publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten);
+ publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten, transferee);
return transfer_result;
}