Add stasis publications for blind and attended transfers.

This creates stasis messages that are sent during a blind or
attended transfer. The stasis messages also are converted to
AMI events.

Review: https://reviewboard.asterisk.org/r/2619

(closes issue ASTERISK-21337)
Reported by Matt Jordan



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393182 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Mark Michelson 12 years ago
parent ca61a05506
commit 6d624eb008

@ -218,7 +218,7 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c
ast_after_bridge_set_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr);
}
if (ast_bridge_transfer_blind(bridge_channel->chan, exten, context, blind_transfer_cb,
if (ast_bridge_transfer_blind(0, bridge_channel->chan, exten, context, blind_transfer_cb,
bridge_channel->chan) != AST_BRIDGE_TRANSFER_SUCCESS &&
!ast_strlen_zero(goto_on_blindxfr)) {
ast_after_bridge_goto_discard(bridge_channel->chan);

@ -10684,7 +10684,7 @@ static int socket_process_helper(struct iax2_thread *thread)
ast_channel_unlock(owner);
ast_mutex_unlock(&iaxsl[fr->callno]);
if (ast_bridge_transfer_blind(owner, ies.called_number,
if (ast_bridge_transfer_blind(1, owner, ies.called_number,
context, NULL, NULL) != AST_BRIDGE_TRANSFER_SUCCESS) {
ast_log(LOG_WARNING, "Blind transfer of '%s' to '%s@%s' failed\n",
ast_channel_name(owner), ies.called_number,

@ -26191,7 +26191,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, uint
}
sip_pvt_unlock(p);
transfer_res = ast_bridge_transfer_blind(transferer, refer_to, refer_to_context, blind_transfer_cb, &cb_data);
transfer_res = ast_bridge_transfer_blind(1, transferer, refer_to, refer_to_context, blind_transfer_cb, &cb_data);
sip_pvt_lock(p);
switch (transfer_res) {

@ -5288,7 +5288,7 @@ static void skinny_transfer_blind(struct skinny_subchannel *sub)
xferee->related = NULL;
ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
res = ast_bridge_transfer_blind(xferee->owner, sub->exten, sub->line->context, NULL, NULL);
res = ast_bridge_transfer_blind(1, xferee->owner, sub->exten, sub->line->context, NULL, NULL);
if (res != AST_BRIDGE_TRANSFER_SUCCESS) {
SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d failed to blind transfer %d to '%s'@'%s' - %d\n",

@ -1499,6 +1499,7 @@ typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data,
* \note Absolutely _NO_ channel locks should be held before
* calling this function.
*
* \param is_external Indicates that transfer was initiated externally
* \param transferer The channel performing the blind transfer
* \param exten The dialplan extension to send the call to
* \param context The dialplan context to send the call to
@ -1507,8 +1508,8 @@ typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data,
* \param user_data Argument for new_channel_cb
* \return The success or failure result of the blind transfer
*/
enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transferer,
const char *exten, const char *context,
enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
struct ast_channel *transferer, const char *exten, const char *context,
transfer_channel_cb new_channel_cb, void *user_data);
/*!

@ -29,6 +29,7 @@ extern "C" {
#include "asterisk/linkedlists.h"
#include "asterisk/channel.h"
#include "asterisk/bridging.h"
#include "asterisk/pbx.h"
/*!
* \brief Structure that contains a snapshot of information about a bridge
@ -210,6 +211,185 @@ void ast_bridge_publish_leave(struct ast_bridge *bridge, struct ast_channel *cha
*/
struct ast_json *ast_bridge_snapshot_to_json(const struct ast_bridge_snapshot *snapshot);
/*!
* \brief Pair showing a bridge snapshot and a specific channel snapshot belonging to the bridge
*/
struct ast_bridge_channel_snapshot_pair {
struct ast_bridge_snapshot *bridge_snapshot;
struct ast_channel_snapshot *channel_snapshot;
};
/*!
* \brief Pair showing a bridge and a specific channel belonging to the bridge
*/
struct ast_bridge_channel_pair {
struct ast_bridge *bridge;
struct ast_channel *channel;
};
/*!
* \brief Message representing blind transfer
*/
struct ast_blind_transfer_message {
AST_DECLARE_STRING_FIELDS(
/*! The destination context for the blind transfer */
AST_STRING_FIELD(context);
/*! The destination extension for the blind transfer */
AST_STRING_FIELD(exten);
);
/*! Result of the blind transfer */
enum ast_transfer_result result;
/*! If 0, was core DTMF transfer, otherwise occurred externally*/
int is_external;
/*! The transferer and its bridge before starting the transfer*/
struct ast_bridge_channel_snapshot_pair transferer;
};
/*!
* \since 12
* \brief Message type for \ref ast_blind_transfer_message.
*
* \retval Message type for \ref ast_blind_transfer_message.
*/
struct stasis_message_type *ast_blind_transfer_type(void);
/*!
* \brief Publish a blind transfer event
*
* \param is_external Whether the blind transfer was initiated externally (e.g. via AMI or native protocol)
* \param result The success or failure of the transfer
* \param to_transferee The bridge between the transferer and transferee plus the transferer channel
* \param context The destination context for the blind transfer
* \param exten The destination extension for the blind transfer
*/
void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *to_transferee, const char *context, const char *exten);
enum ast_attended_transfer_dest_type {
/*! The transfer failed, so there is no appropriate final state */
AST_ATTENDED_TRANSFER_DEST_FAIL,
/*! The transfer results in a single bridge remaining due to a merge or swap */
AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE,
/*! The transfer results in a channel or bridge running an application */
AST_ATTENDED_TRANSFER_DEST_APP,
/*! The transfer results in both bridges remaining with a local channel linking them */
AST_ATTENDED_TRANSFER_DEST_LINK,
};
/*!
* \brief Message representing attended transfer
*/
struct ast_attended_transfer_message {
/*! Result of the attended transfer */
enum ast_transfer_result result;
/*! Indicates if the transfer was initiated externally*/
int is_external;
/*! Bridge between transferer <-> transferee and the transferer channel in that bridge. May be NULL */
struct ast_bridge_channel_snapshot_pair to_transferee;
/*! Bridge between transferer <-> transfer target and the transferer channel in that bridge. May be NULL */
struct ast_bridge_channel_snapshot_pair to_transfer_target;
/*! Indicates the final state of the transfer */
enum ast_attended_transfer_dest_type dest_type;
union {
/*! ID of the surviving bridge. Applicable for AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE */
char bridge[AST_UUID_STR_LEN];
/*! Destination application of transfer. Applicable for AST_ATTENDED_TRANSFER_DEST_APP */
char app[AST_MAX_APP];
/*! Pair of local channels linking the bridges. Applicable for AST_ATTENDED_TRANSFER_DEST_LINK */
struct ast_channel_snapshot *links[2];
} dest;
};
/*!
* \since 12
* \brief Message type for \ref ast_attended_transfer_message.
*
* \retval Message type for \ref ast_attended_transfer_message.
*/
struct stasis_message_type *ast_attended_transfer_type(void);
/*!
* \since 12
* \brief Publish an attended transfer failure
*
* Publish an \ref ast_attended_transfer_message with the dest_type set to
* \c AST_ATTENDED_TRANSFER_DEST_FAIL.
*
* \param is_external Indicates if the transfer was initiated externally
* \param result The result of the transfer. Will always be a type of failure.
* \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
* \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
*/
void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target);
/*!
* \since 12
* \brief Publish an attended transfer that results in two bridges becoming one.
*
* Publish an \ref ast_attended_transfer_message with the dest_type set to
* \c AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE. This type of attended transfer results from
* having two bridges involved and either
*
* \li Merging the two bridges together
* \li Moving a channel from one bridge to the other, thus emptying a bridge
*
* In either case, two bridges enter, one leaves.
*
* \param is_external Indicates if the transfer was initiated externally
* \param result The result of the transfer.
* \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
* \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
* \param final_bridge The bridge that the parties end up in. Will be a bridge from the transferee or target pair.
*/
void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
struct ast_bridge *final_bridge);
/*!
* \since 12
* \brief Publish an attended transfer that results in an application being run
*
* Publish an \ref ast_attended_transfer_message with the dest_type set to
* \c AST_ATTENDED_TRANSFER_DEST_APP. This occurs when an attended transfer
* results in either:
*
* \li A transferee channel leaving a bridge to run an app
* \li A bridge of transferees running an app (via a local channel)
*
* \param is_external Indicates if the transfer was initiated externally
* \param result The result of the transfer.
* \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
* \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
* \param dest_app The application that the channel or bridge is running upon transfer completion.
*/
void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
const char *dest_app);
/*!
* \since 12
* \brief Publish an attended transfer that results in two bridges linked by a local channel
*
* Publish an \ref ast_attended_transfer_message with the dest_type set to
* \c AST_ATTENDED_TRANSFER_DEST_LINK. This occurs when two bridges are involved
* in an attended transfer, but their properties do not allow for the bridges to
* merge or to have channels moved off of the bridge. An example of this occurs when
* attempting to transfer a ConfBridge to another bridge.
*
* When this type of transfer occurs, the two bridges continue to exist after the
* transfer and a local channel is used to link the two bridges together.
*
* \param is_external Indicates if the transfer was initiated externally
* \param result The result of the transfer.
* \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
* \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
* \param locals The local channels linking the bridges together.
*/
void ast_bridge_publish_attended_transfer_link(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
struct ast_channel *locals[2]);
/*!
* \brief Returns the most recent snapshot for the bridge.
*

@ -5923,6 +5923,113 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf
return AST_BRIDGE_TRANSFER_SUCCESS;
}
/*!
* \internal
* \brief Base data to publish for stasis attended transfer messages
*/
struct stasis_attended_transfer_publish_data {
/* The bridge between the transferer and transferee, and the transferer channel in this bridge */
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;
};
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);
ao2_cleanup(publication->to_transferee.bridge);
ao2_cleanup(publication->to_transfer_target.bridge);
}
/*!
* \internal
* \brief Set up base data for an attended transfer stasis publication
*
* \param to_transferee The original transferer channel, which may be bridged to a transferee
* \param to_transferee_bridge The bridge that to_transferee is in.
* \param to_transfer_target The second transferer channel, which may be bridged to a transfer target
* \param to_target_bridge The bridge that to_transfer_target_is in.
* \param[out] publication A structure to hold the other parameters
*/
static void stasis_publish_data_init(struct ast_channel *to_transferee,
struct ast_bridge *to_transferee_bridge, struct ast_channel *to_transfer_target,
struct ast_bridge *to_target_bridge,
struct stasis_attended_transfer_publish_data *publication)
{
memset(publication, 0, sizeof(*publication));
publication->to_transferee.channel = ast_channel_ref(to_transferee);
if (to_transferee_bridge) {
ao2_ref(to_transferee_bridge, +1);
publication->to_transferee.bridge = to_transferee_bridge;
}
publication->to_transfer_target.channel = ast_channel_ref(to_transfer_target);
if (to_target_bridge) {
ao2_ref(to_target_bridge, +1);
publication->to_transfer_target.bridge = to_target_bridge;
}
}
/*
* \internal
* \brief Publish a stasis attended transfer resulting in a bridge merge
*
* \param publication Base data about the attended transfer
* \param final_bridge The surviving bridge of the attended transfer
*/
static void publish_attended_transfer_bridge_merge(struct stasis_attended_transfer_publish_data *publication,
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);
}
/*
* \internal
* \brief Publish a stasis attended transfer to an application
*
* \param publication Base data about the attended transfer
* \param app The app that is running at the conclusion of the transfer
*/
static void publish_attended_transfer_app(struct stasis_attended_transfer_publish_data *publication,
const char *app)
{
ast_bridge_publish_attended_transfer_app(1, AST_BRIDGE_TRANSFER_SUCCESS,
&publication->to_transferee, &publication->to_transfer_target, app);
}
/*
* \internal
* \brief Publish a stasis attended transfer showing a link between bridges
*
* \param publication Base data about the attended transfer
* \param local_channel1 Local channel in the original bridge
* \param local_channel2 Local channel in the second bridge
*/
static void publish_attended_transfer_link(struct stasis_attended_transfer_publish_data *publication,
struct ast_channel *local_channel1, struct ast_channel *local_channel2)
{
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);
}
/*
* \internal
* \brief Publish a stasis attended transfer failure
*
* \param publication Base data about the attended transfer
* \param result The transfer result
*/
static void publish_attended_transfer_fail(struct stasis_attended_transfer_publish_data *publication,
enum ast_transfer_result result)
{
ast_bridge_publish_attended_transfer_fail(1, result, &publication->to_transferee,
&publication->to_transfer_target);
}
/*!
* \brief Perform an attended transfer of a bridge
*
@ -5941,16 +6048,19 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf
* \param chan2 Other transferer channel. May or may not be bridged.
* \param bridge1 Bridge that chan1 is in. Guaranteed to be non-NULL.
* \param bridge2 Bridge that chan2 is in. If NULL, then chan2 is not bridged.
* \param publication Data to publish for a stasis attended transfer message.
* \retval AST_BRIDGE_TRANSFER_FAIL Internal error occurred
* \retval AST_BRIDGE_TRANSFER_SUCCESS Succesfully transferred the bridge
*/
static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *chan1,
struct ast_channel *chan2, struct ast_bridge *bridge1, struct ast_bridge *bridge2)
struct ast_channel *chan2, struct ast_bridge *bridge1, struct ast_bridge *bridge2,
struct stasis_attended_transfer_publish_data *publication)
{
static const char *dest = "_attended@transfer/m";
struct ast_channel *local_chan;
int cause;
int res;
const char *app = NULL;
local_chan = ast_request("Local", ast_channel_nativeformats(chan1), chan1,
dest, &cause);
@ -5962,6 +6072,7 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
if (bridge2) {
res = ast_local_setup_bridge(local_chan, bridge2, chan2, NULL);
} else {
app = ast_strdupa(ast_channel_appl(chan2));
res = ast_local_setup_masquerade(local_chan, chan2);
}
@ -5980,6 +6091,20 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
return AST_BRIDGE_TRANSFER_FAIL;
}
if (bridge2) {
RAII_VAR(struct ast_channel *, local_chan2, NULL, ao2_cleanup);
ast_channel_lock(local_chan);
local_chan2 = ast_local_get_peer(local_chan);
ast_channel_unlock(local_chan);
ast_assert(local_chan2 != NULL);
publish_attended_transfer_link(publication,
local_chan, local_chan2);
} else {
publish_attended_transfer_app(publication, app);
}
return AST_BRIDGE_TRANSFER_SUCCESS;
}
@ -6162,8 +6287,18 @@ static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
return bridge;
}
enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transferer,
const char *exten, const char *context,
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)
{
struct ast_bridge_channel_pair pair;
pair.channel = transferer;
pair.bridge = bridge;
ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten);
}
enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
struct ast_channel *transferer, const char *exten, const char *context,
transfer_channel_cb new_channel_cb, void *user_data)
{
RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
@ -6173,16 +6308,19 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere
int do_bridge_transfer;
int transfer_prohibited;
enum try_parking_result parking_result;
enum ast_transfer_result transfer_result;
bridge = acquire_bridge(transferer);
if (!bridge) {
return AST_BRIDGE_TRANSFER_INVALID;
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
}
ast_channel_lock(transferer);
bridge_channel = ast_channel_get_bridge_channel(transferer);
ast_channel_unlock(transferer);
if (!bridge_channel) {
return AST_BRIDGE_TRANSFER_INVALID;
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
}
/* Take off hold if they are on hold. */
@ -6191,9 +6329,11 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere
parking_result = try_parking(bridge, transferer, exten, context);
switch (parking_result) {
case PARKING_SUCCESS:
return AST_BRIDGE_TRANSFER_SUCCESS;
transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
goto publish;
case PARKING_FAILURE:
return AST_BRIDGE_TRANSFER_FAIL;
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
case PARKING_NOT_APPLICABLE:
default:
break;
@ -6204,10 +6344,12 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere
channels = ast_bridge_peers_nolock(bridge);
if (!channels) {
return AST_BRIDGE_TRANSFER_FAIL;
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
}
if (ao2_container_count(channels) <= 1) {
return AST_BRIDGE_TRANSFER_INVALID;
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
}
transfer_prohibited = ast_test_flag(&bridge->feature_flags,
AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
@ -6217,30 +6359,38 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere
}
if (transfer_prohibited) {
return AST_BRIDGE_TRANSFER_NOT_PERMITTED;
transfer_result = AST_BRIDGE_TRANSFER_NOT_PERMITTED;
goto publish;
}
set_blind_transfer_variables(transferer, channels);
if (do_bridge_transfer) {
return blind_transfer_bridge(transferer, bridge, exten, context,
transfer_result = blind_transfer_bridge(transferer, bridge, exten, context,
new_channel_cb, user_data);
goto publish;
}
/* Reaching this portion means that we're dealing with a two-party bridge */
transferee = get_transferee(channels, transferer);
if (!transferee) {
return AST_BRIDGE_TRANSFER_FAIL;
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
}
if (bridge_channel_queue_blind_transfer(transferee, exten, context,
new_channel_cb, user_data)) {
return AST_BRIDGE_TRANSFER_FAIL;
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
}
ast_bridge_remove(bridge, transferer);
return AST_BRIDGE_TRANSFER_SUCCESS;
transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
publish:
publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten);
return transfer_result;
}
/*!
@ -6297,30 +6447,42 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge
* \param to_target_bridge_channel to_transfer_target's bridge_channel
* \param to_transferee_bridge The bridge between to_transferee and the transferee
* \param to_target_bridge The bridge between to_transfer_target and the transfer_target
* \param publication Data to publish for a stasis attended transfer message
* \return The success or failure of the attended transfer
*/
static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel *to_transferee,
struct ast_bridge_channel *to_transferee_bridge_channel,
struct ast_channel *to_transfer_target,
struct ast_bridge_channel *to_target_bridge_channel,
struct ast_bridge *to_transferee_bridge, struct ast_bridge *to_target_bridge)
struct ast_bridge *to_transferee_bridge, struct ast_bridge *to_target_bridge,
struct stasis_attended_transfer_publish_data *publication)
{
struct ast_bridge_channel *kick_me[] = {
to_transferee_bridge_channel,
to_target_bridge_channel,
};
enum ast_transfer_result res;
struct ast_bridge *final_bridge = NULL;
switch (ast_bridges_allow_optimization(to_transferee_bridge, to_target_bridge)) {
case AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE:
return bridge_swap_attended_transfer(to_transferee_bridge, to_target_bridge_channel, to_transferee);
final_bridge = to_transferee_bridge;
res = bridge_swap_attended_transfer(to_transferee_bridge, to_target_bridge_channel, to_transferee);
goto end;
case AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE:
return bridge_swap_attended_transfer(to_target_bridge, to_transferee_bridge_channel, to_transfer_target);
final_bridge = to_target_bridge;
res = bridge_swap_attended_transfer(to_target_bridge, to_transferee_bridge_channel, to_transfer_target);
goto end;
case AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE:
final_bridge = to_transferee_bridge;
bridge_merge_do(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me));
return AST_BRIDGE_TRANSFER_SUCCESS;
res = AST_BRIDGE_TRANSFER_SUCCESS;
goto end;
case AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE:
final_bridge = to_target_bridge;
bridge_merge_do(to_target_bridge, to_transferee_bridge, kick_me, ARRAY_LEN(kick_me));
return AST_BRIDGE_TRANSFER_SUCCESS;
res = AST_BRIDGE_TRANSFER_SUCCESS;
goto end;
case AST_BRIDGE_OPTIMIZE_PROHIBITED:
default:
/* Just because optimization wasn't doable doesn't necessarily mean
@ -6329,11 +6491,23 @@ static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel
*/
if (to_transferee_bridge->inhibit_merge || to_transferee_bridge->dissolved ||
to_target_bridge->inhibit_merge || to_target_bridge->dissolved) {
return AST_BRIDGE_TRANSFER_INVALID;
res = AST_BRIDGE_TRANSFER_INVALID;
goto end;
}
/* Don't goto end here. attended_transfer_bridge will publish its own
* stasis message if it succeeds
*/
return attended_transfer_bridge(to_transferee, to_transfer_target,
to_transferee_bridge, to_target_bridge);
to_transferee_bridge, to_target_bridge, publication);
}
end:
if (res == AST_BRIDGE_TRANSFER_SUCCESS) {
publish_attended_transfer_bridge_merge(publication, final_bridge);
}
return res;
}
enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee,
@ -6350,13 +6524,20 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
struct ast_channel *chan_unbridged;
int transfer_prohibited;
int do_bridge_transfer;
enum ast_transfer_result res;
const char *app = NULL;
struct stasis_attended_transfer_publish_data publication;
to_transferee_bridge = acquire_bridge(to_transferee);
to_target_bridge = acquire_bridge(to_transfer_target);
stasis_publish_data_init(to_transferee, to_transferee_bridge,
to_transfer_target, to_target_bridge, &publication);
/* They can't both be unbridged, you silly goose! */
if (!to_transferee_bridge && !to_target_bridge) {
return AST_BRIDGE_TRANSFER_INVALID;
res = AST_BRIDGE_TRANSFER_INVALID;
goto end;
}
ast_channel_lock(to_transferee);
@ -6407,20 +6588,20 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
/* Let's get the easy one out of the way first */
if (to_transferee_bridge && to_target_bridge) {
enum ast_transfer_result res;
if (!to_transferee_bridge_channel || !to_target_bridge_channel) {
return AST_BRIDGE_TRANSFER_INVALID;
res = AST_BRIDGE_TRANSFER_INVALID;
goto end;
}
ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel,
to_transfer_target, to_target_bridge_channel,
to_transferee_bridge, to_target_bridge);
to_transferee_bridge, to_target_bridge, &publication);
ast_bridge_unlock(to_transferee_bridge);
ast_bridge_unlock(to_target_bridge);
return res;
goto end;
}
the_bridge = to_transferee_bridge ?: to_target_bridge;
@ -6433,11 +6614,13 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
channels = ast_bridge_peers_nolock(the_bridge);
if (!channels) {
return AST_BRIDGE_TRANSFER_FAIL;
res = AST_BRIDGE_TRANSFER_FAIL;
goto end;
}
chan_count = ao2_container_count(channels);
if (chan_count <= 1) {
return AST_BRIDGE_TRANSFER_INVALID;
res = AST_BRIDGE_TRANSFER_INVALID;
goto end;
}
transfer_prohibited = ast_test_flag(&the_bridge->feature_flags,
AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
@ -6447,24 +6630,41 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
}
if (transfer_prohibited) {
return AST_BRIDGE_TRANSFER_NOT_PERMITTED;
res = AST_BRIDGE_TRANSFER_NOT_PERMITTED;
goto end;
}
if (do_bridge_transfer) {
return attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL);
res = attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL, &publication);
goto end;
}
transferee = get_transferee(channels, chan_bridged);
if (!transferee) {
return AST_BRIDGE_TRANSFER_FAIL;
res = AST_BRIDGE_TRANSFER_FAIL;
goto end;
}
app = ast_strdupa(ast_channel_appl(chan_unbridged));
if (bridge_channel_queue_attended_transfer(transferee, chan_unbridged)) {
return AST_BRIDGE_TRANSFER_FAIL;
res = AST_BRIDGE_TRANSFER_FAIL;
goto end;
}
ast_bridge_remove(the_bridge, chan_bridged);
publish_attended_transfer_app(&publication, app);
return AST_BRIDGE_TRANSFER_SUCCESS;
end:
/* All successful transfer paths have published an appropriate stasis message.
* All failure paths have deferred publishing a stasis message until this point
*/
if (res != AST_BRIDGE_TRANSFER_SUCCESS) {
publish_attended_transfer_fail(&publication, res);
}
stasis_publish_data_cleanup(&publication);
return res;
}
/*!

@ -4064,7 +4064,7 @@ static int action_blind_transfer(struct mansession *s, const struct message *m)
context = ast_channel_context(chan);
}
switch (ast_bridge_transfer_blind(chan, exten, context, NULL, NULL)) {
switch (ast_bridge_transfer_blind(1, chan, exten, context, NULL, NULL)) {
case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
astman_send_error(s, m, "Transfer not permitted");
break;

@ -108,6 +108,11 @@ static struct stasis_message_router *bridge_state_router;
</manager>
***/
/*! \brief The \ref stasis subscription returned by the forwarding of the channel topic
* to the manager topic
*/
static struct stasis_subscription *topic_forwarder;
struct ast_str *ast_manager_build_bridge_state_string(
const struct ast_bridge_snapshot *snapshot,
const char *suffix)
@ -409,10 +414,16 @@ static int manager_bridge_info(struct mansession *s, const struct message *m)
return 0;
}
static void manager_bridging_shutdown(void)
static void manager_bridging_cleanup(void)
{
stasis_message_router_unsubscribe(bridge_state_router);
bridge_state_router = NULL;
stasis_unsubscribe(topic_forwarder);
topic_forwarder = NULL;
}
static void manager_bridging_shutdown(void)
{
ast_manager_unregister("BridgeList");
ast_manager_unregister("BridgeInfo");
}
@ -420,6 +431,8 @@ static void manager_bridging_shutdown(void)
int manager_bridging_init(void)
{
int ret = 0;
struct stasis_topic *manager_topic;
struct stasis_topic *bridge_topic;
if (bridge_state_router) {
/* Already initialized */
@ -427,10 +440,29 @@ int manager_bridging_init(void)
}
ast_register_atexit(manager_bridging_shutdown);
ast_register_cleanup(manager_bridging_cleanup);
manager_topic = ast_manager_get_topic();
if (!manager_topic) {
return -1;
}
bridge_topic = stasis_caching_get_topic(ast_bridge_topic_all_cached());
if (!bridge_topic) {
return -1;
}
bridge_state_router = stasis_message_router_create(
stasis_caching_get_topic(ast_bridge_topic_all_cached()));
topic_forwarder = stasis_forward_all(bridge_topic, manager_topic);
if (!topic_forwarder) {
return -1;
}
/* BUGBUG - This should really route off of the manager_router, but
* can't b/c manager_channels is already routing the
* stasis_cache_update_type() messages. Having a separate router can
* cause some message ordering issues with bridge and channel messages.
*/
bridge_state_router = stasis_message_router_create(bridge_topic);
if (!bridge_state_router) {
return -1;
}

@ -41,6 +41,321 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define SNAPSHOT_CHANNELS_BUCKETS 13
/*** DOCUMENTATION
<managerEvent language="en_US" name="BlindTransfer">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when a blind transfer is complete.</synopsis>
<syntax>
<parameter name="Result">
<para>Indicates if the transfer was successful or if it failed.</para>
<enumlist>
<enum name="Fail"><para>An internal error occurred.</para></enum>
<enum name="Invalid"><para>Invalid configuration for transfer (e.g. Not bridged)</para></enum>
<enum name="Not Permitted"><para>Bridge does not permit transfers</para></enum>
<enum name="Success"><para>Transfer completed successfully</para></enum>
</enumlist>
<note><para>A result of <literal>Success</literal> does not necessarily mean that a target was succesfully
contacted. It means that a party was succesfully placed into the dialplan at the expected location.</para></note>
</parameter>
<parameter name="TransfererChannel">
<para>The name of the channel that performed the transfer</para>
</parameter>
<parameter name="TransfererChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
</parameter>
<parameter name="TransfererCallerIDNum">
</parameter>
<parameter name="TransfererCallerIDName">
</parameter>
<parameter name="TransfererConnectedLineNum">
</parameter>
<parameter name="TransfererConnectedLineName">
</parameter>
<parameter name="TransfererAccountCode">
</parameter>
<parameter name="TransfererContext">
</parameter>
<parameter name="TransfererExten">
</parameter>
<parameter name="TransfererPriority">
</parameter>
<parameter name="TransfererUniqueid">
</parameter>
<parameter name="BridgeUniqueid">
<para>The ID of the bridge where the Transferer performed the transfer</para>
</parameter>
<parameter name="BridgeType">
<para>The type of the bridge where the Transferer performed the transfer</para>
</parameter>
<parameter name="IsExternal">
<para>Indicates if the transfer was performed outside of Asterisk. For instance,
a channel protocol native transfer is external. A DTMF transfer is internal.</para>
<enumlist>
<enum name="Yes" />
<enum name="No" />
</enumlist>
</parameter>
<parameter name="Context">
<para>Destination context for the blind transfer.</para>
</parameter>
<parameter name="Extension">
<para>Destination extension for the blind transfer.</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AttendedTransfer">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when an attended transfer is complete.</synopsis>
<syntax>
<xi:include xpointer="xpointer(docs/managerEvent[@name='BlindTransfer']/managerEventInstance/syntax/parameter[@name='Result'])" />
<parameter name="OrigTransfererChannel">
<para>The original transferer channel that performed the attended transfer.</para>
</parameter>
<parameter name="OrigTransfererChannelState">
<para>A numeric code for the channel's current state, related to DestChannelStateDesc</para>
</parameter>
<parameter name="OrigTransfererChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
</parameter>
<parameter name="OrigTransfererCallerIDNum">
</parameter>
<parameter name="OrigTransfererCallerIDName">
</parameter>
<parameter name="OrigTransfererConnectedLineNum">
</parameter>
<parameter name="OrigTransfererConnectedLineName">
</parameter>
<parameter name="OrigTransfererAccountCode">
</parameter>
<parameter name="OrigTransfererContext">
</parameter>
<parameter name="OrigTransfererExten">
</parameter>
<parameter name="OrigTransfererPriority">
</parameter>
<parameter name="OrigTransfererUniqueid">
</parameter>
<parameter name="BridgeUniqueidOrig">
<para>The ID of the bridge where the Transferer performed the transfer</para>
<note><para>This header will not be present if the original transferer was not in a bridge.</para></note>
</parameter>
<parameter name="BridgeTypeOrig">
<para>The type of the bridge where the Transferer performed the transfer</para>
<note><para>This header will not be present if the original transferer was not in a bridge.</para></note>
</parameter>
<parameter name="SecondTransfererChannel">
<para>The second transferer channel involved in the attended transfer.</para>
</parameter>
<parameter name="SecondTransfererChannelState">
<para>A numeric code for the channel's current state, related to SecondTransfererChannelStateDesc</para>
</parameter>
<parameter name="SecondTransfererChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
</parameter>
<parameter name="SecondTransfererCallerIDNum">
</parameter>
<parameter name="SecondTransfererCallerIDName">
</parameter>
<parameter name="SecondTransfererConnectedLineNum">
</parameter>
<parameter name="SecondTransfererConnectedLineName">
</parameter>
<parameter name="SecondTransfererAccountCode">
</parameter>
<parameter name="SecondTransfererContext">
</parameter>
<parameter name="SecondTransfererExten">
</parameter>
<parameter name="SecondTransfererPriority">
</parameter>
<parameter name="SecondTransfererUniqueid">
</parameter>
<parameter name="BridgeUniqueidSecond">
<para>The unique ID of the bridge that the second transferer channel was in, or <literal>None</literal> if the second transferer channel was not bridged</para>
<note><para>This header will not be present if the second transferer was not in a bridge.</para></note>
</parameter>
<parameter name="BridgeTypeSecond">
<para>The type of the bridge where the Transferer performed the transfer</para>
<note><para>This header will not be present if the second transferer was not in a bridge.</para></note>
</parameter>
<parameter name="DestType">
<para>Indicates the method by which the attended transfer completed.</para>
<enumlist>
<enum name="Bridge"><para>The transfer was accomplished by merging two bridges into one.</para></enum>
<enum name="App"><para>The transfer was accomplished by having a channel or bridge run a dialplan application.</para></enum>
<enum name="Link"><para>The transfer was accomplished by linking two bridges together using a local channel pair.</para></enum>
<enum name="Fail"><para>The transfer failed.</para></enum>
</enumlist>
</parameter>
<parameter name="DestBridgeUniqueid">
<para>Indicates the surviving bridge when bridges were merged to complete the transfer</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Bridge</literal></para></note>
</parameter>
<parameter name="DestApp">
<para>Indicates the application that is running when the transfer completes</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>App</literal></para></note>
</parameter>
<parameter name="LocalOneChannel">
<para>The local channel that is bridged with the original bridge when forming a link between bridges</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneChannelState">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneCallerIDNum">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneCallerIDName">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneConnectedLineNum">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneConnectedLineName">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneAccountCode">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneContext">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneExten">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOnePriority">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneUniqueid">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoChannel">
<para>The local channel that is bridged with the second bridge when forming a link between bridges</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoChannelState">
<para>A numeric code for the channel's current state, related to LocalTwoChannelStateDesc</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoCallerIDNum">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoCallerIDName">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoConnectedLineNum">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoConnectedLineName">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoAccountCode">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoContext">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoExten">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoPriority">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoUniqueid">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
</syntax>
<description>
<para>The headers in this event attempt to describe all the major details of the attended transfer. The two transferer channels
and the two bridges are determined based on their chronological establishment. So consider that Alice calls Bob, and then Alice
transfers the call to Voicemail. The transferer and bridge headers would be arranged as follows:</para>
<para> <replaceable>OrigTransfererChannel</replaceable>: Alice's channel in the bridge with Bob.</para>
<para> <replaceable>BridgeUniqueidOrig</replaceable>: The bridge between Alice and Bob.</para>
<para> <replaceable>SecondTransfererChannel</replaceable>: Alice's channel that called Voicemail.</para>
<para> <replaceable>BridgeUniqueidSecond</replaceable>: Not present, since a call to Voicemail has no bridge.</para>
<para>Now consider if the order were reversed; instead of having Alice call Bob and transfer him to Voicemail, Alice instead
calls her Voicemail and transfers that to Bob. The transferer and bridge headers would be arranged as follows:</para>
<para> <replaceable>OrigTransfererChannel</replaceable>: Alice's channel that called Voicemail.</para>
<para> <replaceable>BridgeUniqueidOrig</replaceable>: Not present, since a call to Voicemail has no bridge.</para>
<para> <replaceable>SecondTransfererChannel</replaceable>: Alice's channel in the bridge with Bob.</para>
<para> <replaceable>BridgeUniqueidSecond</replaceable>: The bridge between Alice and Bob.</para>
</description>
</managerEventInstance>
</managerEvent>
***/
static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_message *message);
static struct ast_manager_event_blob *blind_transfer_to_ami(struct stasis_message *message);
/*!
* @{ \brief Define bridge message types.
*/
@ -48,6 +363,8 @@ STASIS_MESSAGE_TYPE_DEFN(ast_bridge_snapshot_type);
STASIS_MESSAGE_TYPE_DEFN(ast_bridge_merge_message_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_entered_bridge_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_left_bridge_type);
STASIS_MESSAGE_TYPE_DEFN(ast_blind_transfer_type, .to_ami = blind_transfer_to_ami);
STASIS_MESSAGE_TYPE_DEFN(ast_attended_transfer_type, .to_ami = attended_transfer_to_ami);
/*! @} */
/*! \brief Aggregate topic for bridge messages */
@ -352,6 +669,330 @@ struct ast_json *ast_bridge_snapshot_to_json(const struct ast_bridge_snapshot *s
return ast_json_ref(json_bridge);
}
/*!
* \internal
* \brief Allocate the fields of an \ref ast_bridge_channel_snapshot_pair.
*
* \param pair A bridge and channel to get snapshots of
* \param[out] snapshot_pair An allocated snapshot pair.
* \retval 0 Success
* \retval non-zero Failure
*/
static int bridge_channel_snapshot_pair_init(struct ast_bridge_channel_pair *pair, struct ast_bridge_channel_snapshot_pair *snapshot_pair)
{
if (pair->bridge) {
snapshot_pair->bridge_snapshot = ast_bridge_snapshot_create(pair->bridge);
if (!snapshot_pair->bridge_snapshot) {
return -1;
}
}
snapshot_pair->channel_snapshot = ast_channel_snapshot_create(pair->channel);
if (!snapshot_pair->channel_snapshot) {
return -1;
}
return 0;
}
/*!
* \internal
* \brief Free the fields of an \ref ast_bridge_channel_snapshot_pair.
*
* \param pair The snapshot pair whose fields are to be cleaned up
*/
static void bridge_channel_snapshot_pair_cleanup(struct ast_bridge_channel_snapshot_pair *pair)
{
ao2_cleanup(pair->bridge_snapshot);
ao2_cleanup(pair->channel_snapshot);
}
static const char *result_strs[] = {
[AST_BRIDGE_TRANSFER_FAIL] = "Fail",
[AST_BRIDGE_TRANSFER_INVALID] = "Invalid",
[AST_BRIDGE_TRANSFER_NOT_PERMITTED] = "Not Permitted",
[AST_BRIDGE_TRANSFER_SUCCESS] = "Success",
};
static struct ast_manager_event_blob *blind_transfer_to_ami(struct stasis_message *msg)
{
RAII_VAR(struct ast_str *, channel_state, NULL, ast_free_ptr);
RAII_VAR(struct ast_str *, bridge_state, NULL, ast_free_ptr);
struct ast_bridge_blob *blob = stasis_message_data(msg);
const char *exten;
const char *context;
enum ast_transfer_result result;
int is_external;
if (!blob) {
return NULL;
}
channel_state = ast_manager_build_channel_state_string_prefix(blob->channel, "Transferer");
bridge_state = ast_manager_build_bridge_state_string(blob->bridge, "");
if (!channel_state || !bridge_state) {
return NULL;
}
exten = ast_json_string_get(ast_json_object_get(blob->blob, "exten"));
context = ast_json_string_get(ast_json_object_get(blob->blob, "context"));
result = ast_json_integer_get(ast_json_object_get(blob->blob, "result"));
is_external = ast_json_integer_get(ast_json_object_get(blob->blob, "is_external"));
return ast_manager_event_blob_create(EVENT_FLAG_CALL, "BlindTransfer",
"Result: %s\r\n"
"%s"
"%s"
"IsExternal: %s\r\n"
"Context: %s\r\n"
"Extension: %s\r\n",
result_strs[result],
ast_str_buffer(channel_state),
ast_str_buffer(bridge_state),
is_external ? "Yes" : "No",
context,
exten);
}
void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferer, const char *context, const char *exten)
{
RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
json_object = ast_json_pack("{s: s, s: s, s: i, s: i}",
"context", context, "exten", exten, "result", result, "is_external", is_external);
if (!json_object) {
ast_log(LOG_NOTICE, "Failed to create json bridge blob\n");
return;
}
msg = ast_bridge_blob_create(ast_blind_transfer_type(),
transferer->bridge, transferer->channel, json_object);
if (!msg) {
ast_log(LOG_NOTICE, "Failed to create blob msg\n");
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_message *msg)
{
RAII_VAR(struct ast_str *, variable_data, ast_str_create(64), ast_free_ptr);
RAII_VAR(struct ast_str *, transferer1_state, NULL, ast_free_ptr);
RAII_VAR(struct ast_str *, bridge1_state, NULL, ast_free_ptr);
RAII_VAR(struct ast_str *, transferer2_state, NULL, ast_free_ptr);
RAII_VAR(struct ast_str *, bridge2_state, NULL, ast_free_ptr);
RAII_VAR(struct ast_str *, local1_state, NULL, ast_free_ptr);
RAII_VAR(struct ast_str *, local2_state, NULL, ast_free_ptr);
struct ast_attended_transfer_message *transfer_msg = stasis_message_data(msg);
if (!variable_data) {
return NULL;
}
transferer1_state = ast_manager_build_channel_state_string_prefix(transfer_msg->to_transferee.channel_snapshot, "OrigTransferer");
transferer2_state = ast_manager_build_channel_state_string_prefix(transfer_msg->to_transfer_target.channel_snapshot, "SecondTransferer");
if (!transferer1_state || !transferer2_state) {
return NULL;
}
if (transfer_msg->to_transferee.bridge_snapshot) {
bridge1_state = ast_manager_build_bridge_state_string(transfer_msg->to_transferee.bridge_snapshot, "Orig");
if (!bridge1_state) {
return NULL;
}
}
if (transfer_msg->to_transfer_target.bridge_snapshot) {
bridge2_state = ast_manager_build_bridge_state_string(transfer_msg->to_transfer_target.bridge_snapshot, "Second");
if (!bridge2_state) {
return NULL;
}
}
switch (transfer_msg->dest_type) {
case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
ast_str_append(&variable_data, 0, "DestType: Bridge\r\n");
ast_str_append(&variable_data, 0, "DestBridgeUniqueid: %s\r\n", transfer_msg->dest.bridge);
break;
case AST_ATTENDED_TRANSFER_DEST_APP:
ast_str_append(&variable_data, 0, "DestType: App\r\n");
ast_str_append(&variable_data, 0, "DestApp: %s\r\n", transfer_msg->dest.app);
break;
case AST_ATTENDED_TRANSFER_DEST_LINK:
local1_state = ast_manager_build_channel_state_string_prefix(transfer_msg->dest.links[0], "LocalOne");
local2_state = ast_manager_build_channel_state_string_prefix(transfer_msg->dest.links[1], "LocalTwo");
if (!local1_state || !local2_state) {
return NULL;
}
ast_str_append(&variable_data, 0, "DestType: Link\r\n");
ast_str_append(&variable_data, 0, "%s", ast_str_buffer(local1_state));
ast_str_append(&variable_data, 0, "%s", ast_str_buffer(local2_state));
break;
case AST_ATTENDED_TRANSFER_DEST_FAIL:
ast_str_append(&variable_data, 0, "DestType: Fail\r\n");
break;
}
return ast_manager_event_blob_create(EVENT_FLAG_CALL, "AttendedTransfer",
"Result: %s\r\n"
"%s"
"%s"
"%s"
"%s"
"IsExternal: %s\r\n"
"%s\r\n",
result_strs[transfer_msg->result],
ast_str_buffer(transferer1_state),
bridge1_state ? ast_str_buffer(bridge1_state) : "",
ast_str_buffer(transferer2_state),
bridge2_state ? ast_str_buffer(bridge2_state) : "",
transfer_msg->is_external ? "Yes" : "No",
ast_str_buffer(variable_data));
}
static void attended_transfer_dtor(void *obj)
{
struct ast_attended_transfer_message *msg = obj;
int i;
bridge_channel_snapshot_pair_cleanup(&msg->to_transferee);
bridge_channel_snapshot_pair_cleanup(&msg->to_transfer_target);
if (msg->dest_type != AST_ATTENDED_TRANSFER_DEST_LINK) {
return;
}
for (i = 0; i < ARRAY_LEN(msg->dest.links); ++i) {
ao2_cleanup(msg->dest.links[i]);
}
}
static struct ast_attended_transfer_message *attended_transfer_message_create(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target)
{
RAII_VAR(struct ast_attended_transfer_message *, msg, NULL, ao2_cleanup);
msg = ao2_alloc(sizeof(*msg), attended_transfer_dtor);
if (!msg) {
return NULL;
}
if (bridge_channel_snapshot_pair_init(transferee, &msg->to_transferee) ||
bridge_channel_snapshot_pair_init(target, &msg->to_transfer_target)) {
return NULL;
}
msg->is_external = is_external;
msg->result = result;
ao2_ref(msg, +1);
return msg;
}
void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target)
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_FAIL;
msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
struct ast_bridge *final_bridge)
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE;
ast_copy_string(transfer_msg->dest.bridge, final_bridge->uniqueid,
sizeof(transfer_msg->dest.bridge));
msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
const char *dest_app)
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_APP;
ast_copy_string(transfer_msg->dest.app, dest_app, sizeof(transfer_msg->dest.app));
msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
void ast_bridge_publish_attended_transfer_link(int is_external, enum ast_transfer_result result,
struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
struct ast_channel *locals[2])
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
int i;
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_LINK;
for (i = 0; i < 2; ++i) {
transfer_msg->dest.links[i] = ast_channel_snapshot_create(locals[i]);
if (!transfer_msg->dest.links[i]) {
return;
}
}
msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
struct ast_bridge_snapshot *ast_bridge_snapshot_get_latest(const char *uniqueid)
{
RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
@ -387,6 +1028,8 @@ static void stasis_bridging_cleanup(void)
STASIS_MESSAGE_TYPE_CLEANUP(ast_bridge_merge_message_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_entered_bridge_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_left_bridge_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_blind_transfer_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_attended_transfer_type);
}
/*! \brief snapshot ID getter for caching topic */
@ -408,9 +1051,12 @@ int ast_stasis_bridging_init(void)
STASIS_MESSAGE_TYPE_INIT(ast_bridge_merge_message_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_entered_bridge_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_left_bridge_type);
STASIS_MESSAGE_TYPE_INIT(ast_blind_transfer_type);
STASIS_MESSAGE_TYPE_INIT(ast_attended_transfer_type);
bridge_topic_all = stasis_topic_create("ast_bridge_topic_all");
bridge_topic_all_cached = stasis_caching_topic_create(bridge_topic_all, bridge_snapshot_get_id);
bridge_topic_pool = stasis_topic_pool_create(bridge_topic_all);
return !bridge_topic_all
|| !bridge_topic_all_cached
|| !bridge_topic_pool ? -1 : 0;

@ -552,7 +552,7 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi
refer.replaces = replaces;
refer.refer_to = target_uri;
switch (ast_bridge_transfer_blind(session->channel, "external_replaces", context, refer_blind_callback, &refer)) {
switch (ast_bridge_transfer_blind(1, session->channel, "external_replaces", context, refer_blind_callback, &refer)) {
case AST_BRIDGE_TRANSFER_INVALID:
return 400;
case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
@ -594,7 +594,7 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r
refer.progress = progress;
refer.rdata = rdata;
switch (ast_bridge_transfer_blind(session->channel, exten, context, refer_blind_callback, &refer)) {
switch (ast_bridge_transfer_blind(1, session->channel, exten, context, refer_blind_callback, &refer)) {
case AST_BRIDGE_TRANSFER_INVALID:
return 400;
case AST_BRIDGE_TRANSFER_NOT_PERMITTED:

Loading…
Cancel
Save