mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-06 04:30:28 +00:00
Stasis: Add information to blind transfer event
When a blind transfer occurs that is forced to create a local channel pair to satisfy the transfer request, information about the local channel pair is not published. This adds a field to describe that channel to the blind transfer message struct so that this information is conveyed properly to consumers of the blind transfer message. This also fixes a bug in which Stasis() was unable to properly identify the channel that was replacing an existing Stasis-controlled channel due to a blind transfer. Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/3921/ ........ Merged revisions 421537 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@421538 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -300,6 +300,8 @@ struct ast_blind_transfer_message {
|
|||||||
char exten[AST_MAX_EXTENSION];
|
char exten[AST_MAX_EXTENSION];
|
||||||
/*! Transferee channel. NULL if there were multiple transferee channels */
|
/*! Transferee channel. NULL if there were multiple transferee channels */
|
||||||
struct ast_channel_snapshot *transferee;
|
struct ast_channel_snapshot *transferee;
|
||||||
|
/*! The channel replacing the transferer when multiple parties are being transferred */
|
||||||
|
struct ast_channel_snapshot *replace_channel;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -312,11 +314,15 @@ struct ast_blind_transfer_message {
|
|||||||
* \param to_transferee The bridge between the transferer and transferee plus the transferer channel
|
* \param to_transferee The bridge between the transferer and transferee plus the transferer channel
|
||||||
* \param context The destination context for the blind transfer
|
* \param context The destination context for the blind transfer
|
||||||
* \param exten The destination extension for the blind transfer
|
* \param exten The destination extension for the blind transfer
|
||||||
* \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
|
* \param transferee_channel If a single channel is being transferred, this is it. If
|
||||||
|
* multiple parties are being transferred, this is NULL.
|
||||||
|
* \param replace_channel If multiple parties are being transferred or the transfer
|
||||||
|
* cannot reach across the bridge due to bridge flags, this is
|
||||||
|
* the channel connecting their bridge to the destination.
|
||||||
*/
|
*/
|
||||||
void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
|
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,
|
struct ast_bridge_channel_pair *to_transferee, const char *context, const char *exten,
|
||||||
struct ast_channel *transferee_channel);
|
struct ast_channel *transferee_channel, struct ast_channel *replace_channel);
|
||||||
|
|
||||||
enum ast_attended_transfer_dest_type {
|
enum ast_attended_transfer_dest_type {
|
||||||
/*! The transfer failed, so there is no appropriate final state */
|
/*! The transfer failed, so there is no appropriate final state */
|
||||||
|
@@ -3805,6 +3805,26 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe
|
|||||||
return peer;
|
return peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void publish_blind_transfer_full(int is_external, enum ast_transfer_result result,
|
||||||
|
struct ast_channel *transferer, struct ast_bridge *bridge,
|
||||||
|
const char *context, const char *exten, struct ast_channel *transferee_channel,
|
||||||
|
struct ast_channel *replace_channel)
|
||||||
|
{
|
||||||
|
struct ast_bridge_channel_pair pair;
|
||||||
|
|
||||||
|
pair.channel = transferer;
|
||||||
|
pair.bridge = bridge;
|
||||||
|
|
||||||
|
if (bridge) {
|
||||||
|
ast_bridge_lock(bridge);
|
||||||
|
}
|
||||||
|
ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten,
|
||||||
|
transferee_channel, replace_channel);
|
||||||
|
if (bridge) {
|
||||||
|
ast_bridge_unlock(bridge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \internal
|
* \internal
|
||||||
* \brief Transfer an entire bridge to a specific destination.
|
* \brief Transfer an entire bridge to a specific destination.
|
||||||
@@ -3817,16 +3837,21 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe
|
|||||||
* bridges, this method is only used for multi-party bridges since this method would
|
* bridges, this method is only used for multi-party bridges since this method would
|
||||||
* be less efficient for two-party bridges.
|
* be less efficient for two-party bridges.
|
||||||
*
|
*
|
||||||
|
* \param is_external Whether the transfer is externally initiated
|
||||||
* \param transferer The channel performing a transfer
|
* \param transferer The channel performing a transfer
|
||||||
* \param bridge The bridge where the transfer is being performed
|
* \param bridge The bridge where the transfer is being performed
|
||||||
* \param exten The destination extension for the blind transfer
|
* \param exten The destination extension for the blind transfer
|
||||||
|
* \param transferee The party being transferred if there is only one
|
||||||
* \param context The destination context for the blind transfer
|
* \param context The destination context for the blind transfer
|
||||||
* \param hook Framehook to attach to local channel
|
* \param hook Framehook to attach to local channel
|
||||||
|
*
|
||||||
* \return The success or failure of the operation
|
* \return The success or failure of the operation
|
||||||
*/
|
*/
|
||||||
static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transferer,
|
static enum ast_transfer_result blind_transfer_bridge(int is_external,
|
||||||
struct ast_bridge *bridge, const char *exten, const char *context,
|
struct ast_channel *transferer, struct ast_bridge *bridge,
|
||||||
transfer_channel_cb new_channel_cb, struct transfer_channel_data *user_data_wrapper)
|
const char *exten, const char *context, struct ast_channel *transferee,
|
||||||
|
transfer_channel_cb new_channel_cb,
|
||||||
|
struct transfer_channel_data *user_data_wrapper)
|
||||||
{
|
{
|
||||||
struct ast_channel *local;
|
struct ast_channel *local;
|
||||||
char chan_name[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
|
char chan_name[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
|
||||||
@@ -3858,6 +3883,8 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf
|
|||||||
ast_hangup(local);
|
ast_hangup(local);
|
||||||
return AST_BRIDGE_TRANSFER_FAIL;
|
return AST_BRIDGE_TRANSFER_FAIL;
|
||||||
}
|
}
|
||||||
|
publish_blind_transfer_full(is_external, AST_BRIDGE_TRANSFER_SUCCESS, transferer, bridge,
|
||||||
|
context, exten, transferee, local);
|
||||||
return AST_BRIDGE_TRANSFER_SUCCESS;
|
return AST_BRIDGE_TRANSFER_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4214,16 +4241,8 @@ static void publish_blind_transfer(int is_external, enum ast_transfer_result res
|
|||||||
struct ast_channel *transferer, struct ast_bridge *bridge,
|
struct ast_channel *transferer, struct ast_bridge *bridge,
|
||||||
const char *context, const char *exten, struct ast_channel *transferee_channel)
|
const char *context, const char *exten, struct ast_channel *transferee_channel)
|
||||||
{
|
{
|
||||||
struct ast_bridge_channel_pair pair;
|
publish_blind_transfer_full(is_external, result, transferer, bridge, context,
|
||||||
pair.channel = transferer;
|
exten, transferee_channel, NULL);
|
||||||
pair.bridge = bridge;
|
|
||||||
if (bridge) {
|
|
||||||
ast_bridge_lock(bridge);
|
|
||||||
}
|
|
||||||
ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten, transferee_channel);
|
|
||||||
if (bridge) {
|
|
||||||
ast_bridge_unlock(bridge);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
|
enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
|
||||||
@@ -4301,8 +4320,12 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
|
|||||||
set_transfer_variables_all(transferer, channels, 0);
|
set_transfer_variables_all(transferer, channels, 0);
|
||||||
|
|
||||||
if (do_bridge_transfer) {
|
if (do_bridge_transfer) {
|
||||||
transfer_result = blind_transfer_bridge(transferer, bridge, exten, context,
|
/* if blind_transfer_bridge succeeds, it publishes its own message */
|
||||||
new_channel_cb, user_data_wrapper);
|
transfer_result = blind_transfer_bridge(is_external, transferer, bridge,
|
||||||
|
exten, context, transferee, new_channel_cb, user_data_wrapper);
|
||||||
|
if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS) {
|
||||||
|
return transfer_result;
|
||||||
|
}
|
||||||
goto publish;
|
goto publish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -636,7 +636,10 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg,
|
|||||||
const struct stasis_message_sanitizer *sanitize)
|
const struct stasis_message_sanitizer *sanitize)
|
||||||
{
|
{
|
||||||
struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
|
struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
|
||||||
struct ast_json *json_transferer, *json_transferee = NULL, *out;
|
struct ast_json *json_transferer;
|
||||||
|
struct ast_json *json_transferee = NULL;
|
||||||
|
struct ast_json *out;
|
||||||
|
struct ast_json *json_replace = NULL;
|
||||||
const struct timeval *tv = stasis_message_timestamp(msg);
|
const struct timeval *tv = stasis_message_timestamp(msg);
|
||||||
|
|
||||||
json_transferer = ast_channel_snapshot_to_json(transfer_msg->to_transferee.channel_snapshot, sanitize);
|
json_transferer = ast_channel_snapshot_to_json(transfer_msg->to_transferee.channel_snapshot, sanitize);
|
||||||
@@ -647,6 +650,16 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg,
|
|||||||
if (transfer_msg->transferee) {
|
if (transfer_msg->transferee) {
|
||||||
json_transferee = ast_channel_snapshot_to_json(transfer_msg->transferee, sanitize);
|
json_transferee = ast_channel_snapshot_to_json(transfer_msg->transferee, sanitize);
|
||||||
if (!json_transferee) {
|
if (!json_transferee) {
|
||||||
|
ast_json_unref(json_transferer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transfer_msg->replace_channel) {
|
||||||
|
json_replace = ast_channel_snapshot_to_json(transfer_msg->replace_channel, sanitize);
|
||||||
|
if (!json_replace) {
|
||||||
|
ast_json_unref(json_transferee);
|
||||||
|
ast_json_unref(json_transferer);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -661,10 +674,18 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg,
|
|||||||
"is_external", ast_json_boolean(transfer_msg->is_external));
|
"is_external", ast_json_boolean(transfer_msg->is_external));
|
||||||
|
|
||||||
if (!out) {
|
if (!out) {
|
||||||
|
ast_json_unref(json_transferee);
|
||||||
|
ast_json_unref(json_replace);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json_transferee && ast_json_object_set(out, "transferee", json_transferee)) {
|
if (json_transferee && ast_json_object_set(out, "transferee", json_transferee)) {
|
||||||
|
ast_json_unref(out);
|
||||||
|
ast_json_unref(json_replace);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_replace && ast_json_object_set(out, "replace_channel", json_replace)) {
|
||||||
ast_json_unref(out);
|
ast_json_unref(out);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -741,7 +762,7 @@ static void blind_transfer_dtor(void *obj)
|
|||||||
|
|
||||||
void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
|
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,
|
struct ast_bridge_channel_pair *transferer, const char *context, const char *exten,
|
||||||
struct ast_channel *transferee_channel)
|
struct ast_channel *transferee_channel, struct ast_channel *replace_channel)
|
||||||
{
|
{
|
||||||
struct ast_blind_transfer_message *msg;
|
struct ast_blind_transfer_message *msg;
|
||||||
struct stasis_message *stasis;
|
struct stasis_message *stasis;
|
||||||
@@ -759,6 +780,9 @@ void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result
|
|||||||
if (transferee_channel) {
|
if (transferee_channel) {
|
||||||
msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee_channel));
|
msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee_channel));
|
||||||
}
|
}
|
||||||
|
if (replace_channel) {
|
||||||
|
msg->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(replace_channel));
|
||||||
|
}
|
||||||
msg->is_external = is_external;
|
msg->is_external = is_external;
|
||||||
msg->result = result;
|
msg->result = result;
|
||||||
ast_copy_string(msg->context, context, sizeof(msg->context));
|
ast_copy_string(msg->context, context, sizeof(msg->context));
|
||||||
|
@@ -2062,6 +2062,15 @@ int ast_ari_validate_bridge_blind_transfer(struct ast_json *json)
|
|||||||
res = 0;
|
res = 0;
|
||||||
}
|
}
|
||||||
} else
|
} 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 BridgeBlindTransfer field replace_channel failed validation\n");
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
} else
|
||||||
if (strcmp("result", ast_json_object_iter_key(iter)) == 0) {
|
if (strcmp("result", ast_json_object_iter_key(iter)) == 0) {
|
||||||
int prop_is_valid;
|
int prop_is_valid;
|
||||||
has_result = 1;
|
has_result = 1;
|
||||||
|
@@ -1304,6 +1304,7 @@ ari_validator ast_ari_validate_application_fn(void);
|
|||||||
* - context: string (required)
|
* - context: string (required)
|
||||||
* - exten: string (required)
|
* - exten: string (required)
|
||||||
* - is_external: boolean (required)
|
* - is_external: boolean (required)
|
||||||
|
* - replace_channel: Channel
|
||||||
* - result: string (required)
|
* - result: string (required)
|
||||||
* - transferee: Channel
|
* - transferee: Channel
|
||||||
* BridgeCreated
|
* BridgeCreated
|
||||||
|
@@ -696,19 +696,6 @@ static int bridge_app_subscribed_involved(struct stasis_app *app, struct ast_bri
|
|||||||
return subscribed;
|
return subscribed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bridge_blind_transfer_handler(void *data, struct stasis_subscription *sub,
|
|
||||||
struct stasis_message *message)
|
|
||||||
{
|
|
||||||
struct stasis_app *app = data;
|
|
||||||
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, 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,
|
static void set_replacement_channel(struct ast_channel_snapshot *to_be_replaced,
|
||||||
struct ast_channel_snapshot *replacing)
|
struct ast_channel_snapshot *replacing)
|
||||||
{
|
{
|
||||||
@@ -726,6 +713,24 @@ static void set_replacement_channel(struct ast_channel_snapshot *to_be_replaced,
|
|||||||
ao2_cleanup(control);
|
ao2_cleanup(control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bridge_blind_transfer_handler(void *data, struct stasis_subscription *sub,
|
||||||
|
struct stasis_message *message)
|
||||||
|
{
|
||||||
|
struct stasis_app *app = data;
|
||||||
|
struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
|
||||||
|
struct ast_bridge_snapshot *bridge = transfer_msg->to_transferee.bridge_snapshot;
|
||||||
|
|
||||||
|
if (transfer_msg->replace_channel) {
|
||||||
|
set_replacement_channel(transfer_msg->to_transferee.channel_snapshot,
|
||||||
|
transfer_msg->replace_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 bridge_attended_transfer_handler(void *data, struct stasis_subscription *sub,
|
static void bridge_attended_transfer_handler(void *data, struct stasis_subscription *sub,
|
||||||
struct stasis_message *message)
|
struct stasis_message *message)
|
||||||
{
|
{
|
||||||
|
@@ -285,6 +285,11 @@
|
|||||||
"required": true,
|
"required": true,
|
||||||
"type": "Channel"
|
"type": "Channel"
|
||||||
},
|
},
|
||||||
|
"replace_channel": {
|
||||||
|
"description": "The channel that is replacing transferer when the transferee(s) can not be transferred directly",
|
||||||
|
"required": false,
|
||||||
|
"type": "Channel"
|
||||||
|
},
|
||||||
"transferee": {
|
"transferee": {
|
||||||
"description": "The channel that is being transferred",
|
"description": "The channel that is being transferred",
|
||||||
"required": false,
|
"required": false,
|
||||||
|
@@ -1257,7 +1257,7 @@ AST_TEST_DEFINE(test_cel_blind_transfer)
|
|||||||
pair.channel = chan_alice;
|
pair.channel = chan_alice;
|
||||||
ast_bridge_lock(bridge);
|
ast_bridge_lock(bridge);
|
||||||
ast_bridge_publish_blind_transfer(1, AST_BRIDGE_TRANSFER_SUCCESS,
|
ast_bridge_publish_blind_transfer(1, AST_BRIDGE_TRANSFER_SUCCESS,
|
||||||
&pair, "transfer_context", "transfer_extension", NULL);
|
&pair, "transfer_context", "transfer_extension", NULL, NULL);
|
||||||
ast_bridge_unlock(bridge);
|
ast_bridge_unlock(bridge);
|
||||||
BLINDTRANSFER_EVENT(chan_alice, bridge, "transfer_extension", "transfer_context");
|
BLINDTRANSFER_EVENT(chan_alice, bridge, "transfer_extension", "transfer_context");
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user