res_pjsip_refer: Fix bugs involving Parking/PJSIP/transfers

PJSIP would never send the final 200 Notify for a blind transfer
when transferring to parking. This patch fixes that. In addition,
it fixes a reference leak when performing blind transfers to
non-bridging extensions.

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

Merged revisions 414400 from http://svn.asterisk.org/svn/asterisk/branches/12


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@414403 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/97/197/1
Jonathan Rose 12 years ago
parent 912bbdd1dd
commit d00882108f

@ -9741,7 +9741,7 @@ static void *analog_ss_thread(void *data)
bridge_channel = ast_channel_get_bridge_channel(p->subs[SUB_THREEWAY].owner); bridge_channel = ast_channel_get_bridge_channel(p->subs[SUB_THREEWAY].owner);
ast_channel_unlock(p->subs[SUB_THREEWAY].owner); ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
if (bridge_channel) { if (bridge_channel) {
if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) { if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten, NULL, NULL)) {
/* /*
* Swap things around between the three-way and real call so we * Swap things around between the three-way and real call so we
* can hear where the channel got parked. * can hear where the channel got parked.

@ -3167,7 +3167,7 @@ static void *mgcp_ss(void *data)
ast_channel_lock(chan); ast_channel_lock(chan);
bridge_channel = ast_channel_get_bridge_channel(chan); bridge_channel = ast_channel_get_bridge_channel(chan);
ast_channel_unlock(chan); ast_channel_unlock(chan);
if (bridge_channel && !ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), p->dtmf_buf)) { if (bridge_channel && !ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), p->dtmf_buf, NULL, NULL)) {
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan)); ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
} }
break; break;

@ -26081,10 +26081,10 @@ struct blind_transfer_cb_data {
* \param user_data A blind_transfer_cb_data struct * \param user_data A blind_transfer_cb_data struct
* \param transfer_type Unused * \param transfer_type Unused
*/ */
static void blind_transfer_cb(struct ast_channel *chan, void *user_data, static void blind_transfer_cb(struct ast_channel *chan, struct transfer_channel_data *user_data_wrapper,
enum ast_transfer_type transfer_type) enum ast_transfer_type transfer_type)
{ {
struct blind_transfer_cb_data *cb_data = user_data; struct blind_transfer_cb_data *cb_data = user_data_wrapper->data;
pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes"); pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes");
pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REFERER", cb_data->referred_by); pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REFERER", cb_data->referred_by);

@ -2259,7 +2259,7 @@ static void *__analog_ss_thread(void *data)
bridge_channel = ast_channel_get_bridge_channel(p->subs[ANALOG_SUB_THREEWAY].owner); bridge_channel = ast_channel_get_bridge_channel(p->subs[ANALOG_SUB_THREEWAY].owner);
ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
if (bridge_channel) { if (bridge_channel) {
if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) { if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten, NULL, NULL)) {
/* /*
* Swap things around between the three-way and real call so we * Swap things around between the three-way and real call so we
* can hear where the channel got parked. * can hear where the channel got parked.

@ -903,6 +903,22 @@ enum ast_transfer_type {
AST_BRIDGE_TRANSFER_MULTI_PARTY, AST_BRIDGE_TRANSFER_MULTI_PARTY,
}; };
/*!
* \brief AO2 object that wraps data for transfer_channel_cb
*/
struct transfer_channel_data {
void *data; /*! Data to be used by the transfer_channel_cb -- note that this
* pointer is going to be pointing to something on the stack, so
* it must not be used at any point after returning from the
* transfer_channel_cb. */
int completed; /*! Initially 0, This will be set to 1 by either the transfer
* code or by transfer code hooks (e.g. parking) when the
* transfer is completed and any remaining actions have taken
* place (e.g. parking announcements). It will never be reset
* to 0. This is used for deferring progress for channel
* drivers that support deferred progress. */
};
/*! /*!
* \brief Callback function type called during blind transfers * \brief Callback function type called during blind transfers
* *
@ -914,7 +930,7 @@ enum ast_transfer_type {
* \param user_data User-provided data needed in the callback * \param user_data User-provided data needed in the callback
* \param transfer_type The type of transfer being completed * \param transfer_type The type of transfer being completed
*/ */
typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data, typedef void (*transfer_channel_cb)(struct ast_channel *chan, struct transfer_channel_data *user_data,
enum ast_transfer_type transfer_type); enum ast_transfer_type transfer_type);
/*! /*!

@ -24,6 +24,7 @@
*/ */
#include "asterisk/stringfields.h" #include "asterisk/stringfields.h"
#include "asterisk/bridge.h"
/*! /*!
* \brief The default parking application that Asterisk expects. * \brief The default parking application that Asterisk expects.
@ -163,6 +164,8 @@ struct ast_parking_bridge_feature_fn_table {
* \param parker The \ref bridge_channel object that is initiating the parking * \param parker The \ref bridge_channel object that is initiating the parking
* \param context The context to blind transfer to * \param context The context to blind transfer to
* \param exten The extension to blind transfer to * \param exten The extension to blind transfer to
* \param parked_channel_cb Execute the following function on the the channel that gets parked
* \param parked_channel_data Data for the parked_channel_cb
* *
* \note If the bridge \ref parker is in has more than one other occupant, the entire * \note If the bridge \ref parker is in has more than one other occupant, the entire
* bridge will be parked using a Local channel * bridge will be parked using a Local channel
@ -172,7 +175,8 @@ struct ast_parking_bridge_feature_fn_table {
* \retval 0 on success * \retval 0 on success
* \retval non-zero on error * \retval non-zero on error
*/ */
int (* parking_blind_transfer_park)(struct ast_bridge_channel *parker, const char *context, const char *exten); int (* parking_blind_transfer_park)(struct ast_bridge_channel *parker, const char *context,
const char *exten, transfer_channel_cb parked_channel_cb, struct transfer_channel_data *parked_channel_data);
/*! /*!
* \brief Perform a direct park on a channel in a bridge. * \brief Perform a direct park on a channel in a bridge.
@ -224,6 +228,9 @@ int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t
* \param parker The \ref bridge_channel object that is initiating the parking * \param parker The \ref bridge_channel object that is initiating the parking
* \param context The context to blind transfer to * \param context The context to blind transfer to
* \param exten The extension to blind transfer to * \param exten The extension to blind transfer to
* \param exten The extension to blind transfer to
* \param parked_channel_cb Execute the following function on the the channel that gets parked
* \param parked_channel_data Data for the parked_channel_cb
* *
* \note If the bridge \ref parker is in has more than one other occupant, the entire * \note If the bridge \ref parker is in has more than one other occupant, the entire
* bridge will be parked using a Local channel * bridge will be parked using a Local channel
@ -233,7 +240,8 @@ int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t
* \retval 0 on success * \retval 0 on success
* \retval non-zero on error * \retval non-zero on error
*/ */
int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const char *context, const char *exten); int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const char *context,
const char *exten, transfer_channel_cb parked_channel_cb, struct transfer_channel_data *parked_channel_data);
/*! /*!
* \brief Perform a direct park on a channel in a bridge. * \brief Perform a direct park on a channel in a bridge.

@ -3718,7 +3718,7 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe
*/ */
static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transferer, static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transferer,
struct ast_bridge *bridge, const char *exten, const char *context, struct ast_bridge *bridge, const char *exten, const char *context,
transfer_channel_cb new_channel_cb, void *user_data) 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];
@ -3734,7 +3734,7 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf
pbx_builtin_setvar_helper(local, BLINDTRANSFER, ast_channel_name(transferer)); pbx_builtin_setvar_helper(local, BLINDTRANSFER, ast_channel_name(transferer));
if (new_channel_cb) { if (new_channel_cb) {
new_channel_cb(local, user_data, AST_BRIDGE_TRANSFER_MULTI_PARTY); new_channel_cb(local, user_data_wrapper, AST_BRIDGE_TRANSFER_MULTI_PARTY);
} }
if (ast_call(local, chan_name, 0)) { if (ast_call(local, chan_name, 0)) {
@ -3968,7 +3968,9 @@ static struct ast_channel *get_transferee(struct ao2_container *channels, struct
return transferee; return transferee;
} }
static enum ast_transfer_result try_parking(struct ast_channel *transferer, const char *context, const char *exten) static enum ast_transfer_result try_parking(struct ast_channel *transferer,
const char *context, const char *exten, transfer_channel_cb new_channel_cb,
struct transfer_channel_data *user_data_wrapper)
{ {
RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup);
@ -3985,7 +3987,7 @@ static enum ast_transfer_result try_parking(struct ast_channel *transferer, cons
} }
if (ast_parking_blind_transfer_park(transferer_bridge_channel, if (ast_parking_blind_transfer_park(transferer_bridge_channel,
context, exten)) { context, exten, new_channel_cb, user_data_wrapper)) {
return AST_BRIDGE_TRANSFER_FAIL; return AST_BRIDGE_TRANSFER_FAIL;
} }
@ -4089,6 +4091,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel *, transferee, NULL, ast_channel_cleanup); RAII_VAR(struct ast_channel *, transferee, NULL, ast_channel_cleanup);
RAII_VAR(struct transfer_channel_data *, user_data_wrapper, NULL, ao2_cleanup);
int do_bridge_transfer; int do_bridge_transfer;
int transfer_prohibited; int transfer_prohibited;
enum ast_transfer_result transfer_result; enum ast_transfer_result transfer_result;
@ -4106,14 +4109,25 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
goto publish; goto publish;
} }
user_data_wrapper = ao2_alloc(sizeof(*user_data_wrapper), NULL);
if (!user_data_wrapper) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
}
user_data_wrapper->data = user_data;
/* Take off hold if they are on hold. */ /* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(bridge_channel); ast_bridge_channel_write_unhold(bridge_channel);
transfer_result = try_parking(transferer, context, exten); transfer_result = try_parking(transferer, context, exten, new_channel_cb, user_data_wrapper);
if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS) { if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS) {
goto publish; goto publish;
} }
/* Since parking didn't take control of the user_data_wrapper, we are just going to raise the completed flag now. */
user_data_wrapper->completed = 1;
{ {
SCOPED_LOCK(lock, bridge, ast_bridge_lock, ast_bridge_unlock); SCOPED_LOCK(lock, bridge, ast_bridge_lock, ast_bridge_unlock);
@ -4142,7 +4156,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
if (do_bridge_transfer) { if (do_bridge_transfer) {
transfer_result = blind_transfer_bridge(transferer, bridge, exten, context, transfer_result = blind_transfer_bridge(transferer, bridge, exten, context,
new_channel_cb, user_data); new_channel_cb, user_data_wrapper);
goto publish; goto publish;
} }
@ -4155,7 +4169,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
} }
if (bridge_channel_internal_queue_blind_transfer(transferee, exten, context, if (bridge_channel_internal_queue_blind_transfer(transferee, exten, context,
new_channel_cb, user_data)) { new_channel_cb, user_data_wrapper)) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL; transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish; goto publish;
} }

@ -3145,10 +3145,10 @@ static int feature_attended_transfer(struct ast_bridge_channel *bridge_channel,
return 0; return 0;
} }
static void blind_transfer_cb(struct ast_channel *new_channel, void *user_data, static void blind_transfer_cb(struct ast_channel *new_channel, struct transfer_channel_data *user_data_wrapper,
enum ast_transfer_type transfer_type) enum ast_transfer_type transfer_type)
{ {
struct ast_channel *transferer_channel = user_data; struct ast_channel *transferer_channel = user_data_wrapper->data;
if (transfer_type == AST_BRIDGE_TRANSFER_MULTI_PARTY) { if (transfer_type == AST_BRIDGE_TRANSFER_MULTI_PARTY) {
copy_caller_data(new_channel, transferer_channel); copy_caller_data(new_channel, transferer_channel);

@ -142,7 +142,9 @@ int ast_parking_park_bridge_channel(struct ast_bridge_channel *parkee, const cha
return table->parking_park_bridge_channel(parkee, parkee_uuid, parker_uuid, app_data); return table->parking_park_bridge_channel(parkee, parkee_uuid, parker_uuid, app_data);
} }
int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const char *context, const char *exten) int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker,
const char *context, const char *exten, transfer_channel_cb parked_channel_cb,
struct transfer_channel_data *parked_channel_data)
{ {
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table, RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
ao2_global_obj_ref(parking_provider), ao2_cleanup); ao2_global_obj_ref(parking_provider), ao2_cleanup);
@ -153,10 +155,10 @@ int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const cha
if (table->module_info) { if (table->module_info) {
SCOPED_MODULE_USE(table->module_info->self); SCOPED_MODULE_USE(table->module_info->self);
return table->parking_blind_transfer_park(parker, context, exten); return table->parking_blind_transfer_park(parker, context, exten, parked_channel_cb, parked_channel_data);
} }
return table->parking_blind_transfer_park(parker, context, exten); return table->parking_blind_transfer_park(parker, context, exten, parked_channel_cb, parked_channel_data);
} }
int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t length) int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t length)

@ -516,6 +516,7 @@ static int park_app_exec(struct ast_channel *chan, const char *data)
if (!silence_announcements && !transferer) { if (!silence_announcements && !transferer) {
ast_stream_and_wait(chan, "pbx-parkingfailed", ""); ast_stream_and_wait(chan, "pbx-parkingfailed", "");
} }
publish_parked_call_failure(chan);
return 0; return 0;
} }
@ -523,6 +524,7 @@ static int park_app_exec(struct ast_channel *chan, const char *data)
res = ast_bridge_features_init(&chan_features); res = ast_bridge_features_init(&chan_features);
if (res) { if (res) {
ast_bridge_features_cleanup(&chan_features); ast_bridge_features_cleanup(&chan_features);
publish_parked_call_failure(chan);
return -1; return -1;
} }

@ -49,6 +49,7 @@ struct parked_subscription_datastore {
}; };
struct parked_subscription_data { struct parked_subscription_data {
struct transfer_channel_data *transfer_data;
char *parkee_uuid; char *parkee_uuid;
int hangup_after:1; int hangup_after:1;
char parker_uuid[0]; char parker_uuid[0];
@ -89,7 +90,7 @@ static void parker_parked_call_message_response(struct ast_parked_call_payload *
const char *parkee_to_act_on = data->parkee_uuid; const char *parkee_to_act_on = data->parkee_uuid;
char saynum_buf[16]; char saynum_buf[16];
struct ast_channel_snapshot *parkee_snapshot = message->parkee; struct ast_channel_snapshot *parkee_snapshot = message->parkee;
RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, parker, NULL, ast_channel_cleanup);
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
if (strcmp(parkee_to_act_on, parkee_snapshot->uniqueid)) { if (strcmp(parkee_to_act_on, parkee_snapshot->uniqueid)) {
@ -113,22 +114,35 @@ static void parker_parked_call_message_response(struct ast_parked_call_payload *
return; return;
} }
/* This subscription callback will block for the duration of the announcement if
* parked_subscription_data is tracking a transfer_channel_data struct. */
if (message->event_type == PARKED_CALL) { if (message->event_type == PARKED_CALL) {
/* queue the saynum on the bridge channel and hangup */ /* queue the saynum on the bridge channel and hangup */
snprintf(saynum_buf, sizeof(saynum_buf), "%d %u", data->hangup_after, message->parkingspace); snprintf(saynum_buf, sizeof(saynum_buf), "%d %u", data->hangup_after, message->parkingspace);
if (!data->transfer_data) {
ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL); ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL);
wipe_subscription_datastore(bridge_channel->chan); } else {
ast_bridge_channel_queue_playfile_sync(bridge_channel, say_parking_space, saynum_buf, NULL);
data->transfer_data->completed = 1;
} }
wipe_subscription_datastore(parker);
if (message->event_type == PARKED_CALL_FAILED) { } else if (message->event_type == PARKED_CALL_FAILED) {
if (!data->transfer_data) {
ast_bridge_channel_queue_playfile(bridge_channel, NULL, "pbx-parkingfailed", NULL); ast_bridge_channel_queue_playfile(bridge_channel, NULL, "pbx-parkingfailed", NULL);
wipe_subscription_datastore(bridge_channel->chan); } else {
ast_bridge_channel_queue_playfile_sync(bridge_channel, NULL, "pbx-parkingfailed", NULL);
data->transfer_data->completed = 1;
}
wipe_subscription_datastore(parker);
} }
} }
static void parker_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message) static void parker_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
{ {
if (stasis_subscription_final_message(sub, message)) { if (stasis_subscription_final_message(sub, message)) {
struct parked_subscription_data *ps_data = data;
ao2_cleanup(ps_data->transfer_data);
ps_data->transfer_data = NULL;
ast_free(data); ast_free(data);
return; return;
} }
@ -139,7 +153,8 @@ static void parker_update_cb(void *data, struct stasis_subscription *sub, struct
} }
} }
int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid, int hangup_after) static int create_parked_subscription_full(struct ast_channel *chan, const char *parkee_uuid, int hangup_after,
struct transfer_channel_data *parked_channel_data)
{ {
struct ast_datastore *datastore; struct ast_datastore *datastore;
struct parked_subscription_datastore *parked_datastore; struct parked_subscription_datastore *parked_datastore;
@ -167,6 +182,11 @@ int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid
return -1; return -1;
} }
if (parked_channel_data) {
subscription_data->transfer_data = parked_channel_data;
ao2_ref(parked_channel_data, +1);
}
subscription_data->hangup_after = hangup_after; subscription_data->hangup_after = hangup_after;
subscription_data->parkee_uuid = subscription_data->parker_uuid + parker_uuid_size; subscription_data->parkee_uuid = subscription_data->parker_uuid + parker_uuid_size;
strcpy(subscription_data->parkee_uuid, parkee_uuid); strcpy(subscription_data->parkee_uuid, parkee_uuid);
@ -185,13 +205,18 @@ int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid
return 0; return 0;
} }
int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid, int hangup_after)
{
return create_parked_subscription_full(chan, parkee_uuid, hangup_after, NULL);
}
/*! /*!
* \internal * \internal
* \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly
* identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the * identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the
* local channel and the channel that instigated the park. * local channel and the channel that instigated the park.
*/ */
static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten) static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten, struct transfer_channel_data *parked_channel_data)
{ {
char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1];
struct ast_channel *parkee; struct ast_channel *parkee;
@ -220,7 +245,11 @@ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const
ast_channel_unlock(parkee); ast_channel_unlock(parkee);
/* We need to have the parker subscribe to the new local channel before hand. */ /* We need to have the parker subscribe to the new local channel before hand. */
create_parked_subscription(parker, ast_channel_uniqueid(parkee_side_2), 1); if (create_parked_subscription_full(parker, ast_channel_uniqueid(parkee_side_2), 1, parked_channel_data)) {
ast_channel_unref(parkee_side_2);
ast_hangup(parkee);
return NULL;
}
ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0); ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0);
@ -272,14 +301,21 @@ static int parking_is_exten_park(const char *context, const char *exten)
* \param bridge_channel The bridge_channel representing the channel performing the park * \param bridge_channel The bridge_channel representing the channel performing the park
* \param context The context to blind transfer to * \param context The context to blind transfer to
* \param exten The extension to blind transfer to * \param exten The extension to blind transfer to
* \param parked_channel_cb Optional callback executed prior to sending the parked channel into the bridge
* \param parked_channel_data Data for the parked_channel_cb
* *
* \retval 0 on success * \retval 0 on success
* \retval non-zero on error * \retval non-zero on error
*/ */
static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel, static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel,
const char *context, const char *exten) const char *context, const char *exten, transfer_channel_cb parked_channel_cb,
struct transfer_channel_data *parked_channel_data)
{ {
RAII_VAR(struct ast_bridge_channel *, other, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge_channel *, other, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel *, other_chan, NULL, ast_channel_cleanup);
struct ast_exten *e;
struct pbx_find_info find_info = { .stacklen = 0 };
int peer_count; int peer_count;
if (ast_strlen_zero(context) || ast_strlen_zero(exten)) { if (ast_strlen_zero(context) || ast_strlen_zero(exten)) {
@ -299,6 +335,8 @@ static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel
if (peer_count == 2) { if (peer_count == 2) {
other = ast_bridge_channel_peer(bridge_channel); other = ast_bridge_channel_peer(bridge_channel);
ao2_ref(other, +1); ao2_ref(other, +1);
other_chan = other->chan;
ast_channel_ref(other_chan);
} }
ast_bridge_unlock(bridge_channel->bridge); ast_bridge_unlock(bridge_channel->bridge);
@ -313,34 +351,48 @@ static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel
if (peer_count > 2) { if (peer_count > 2) {
struct ast_channel *transfer_chan = NULL; struct ast_channel *transfer_chan = NULL;
transfer_chan = park_local_transfer(bridge_channel->chan, context, exten); transfer_chan = park_local_transfer(bridge_channel->chan, context, exten, parked_channel_data);
if (!transfer_chan) { if (!transfer_chan) {
return -1; return -1;
} }
ast_channel_ref(transfer_chan);
if (parked_channel_cb) {
parked_channel_cb(transfer_chan, parked_channel_data, AST_BRIDGE_TRANSFER_MULTI_PARTY);
}
if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL, if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
ast_hangup(transfer_chan); ast_hangup(transfer_chan);
ast_channel_unref(transfer_chan);
return -1; return -1;
} }
ast_channel_unref(transfer_chan);
return 0; return 0;
} }
/* Subscribe to park messages with the other channel entering */ /* Subscribe to park messages with the other channel entering */
if (create_parked_subscription(bridge_channel->chan, ast_channel_uniqueid(other->chan), 1)) { if (create_parked_subscription_full(bridge_channel->chan, ast_channel_uniqueid(other->chan), 1, parked_channel_data)) {
return -1; return -1;
} }
if (parked_channel_cb) {
parked_channel_cb(other_chan, parked_channel_data, AST_BRIDGE_TRANSFER_SINGLE_PARTY);
}
e = pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH);
/* Write the park frame with the intended recipient and other data out to the bridge. */ /* Write the park frame with the intended recipient and other data out to the bridge. */
ast_bridge_channel_write_park(bridge_channel, ast_bridge_channel_write_park(bridge_channel,
ast_channel_uniqueid(other->chan), ast_channel_uniqueid(other_chan),
ast_channel_uniqueid(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan),
NULL); e ? ast_get_extension_app_data(e) : NULL);
return 0; return 0;
} }
/*! /*!
* \internal * \internal
* \since 12.0.0 * \since 12.0.0
@ -444,7 +496,7 @@ static int parking_park_call(struct ast_bridge_channel *parker, char *exten, siz
if (exten) { if (exten) {
ast_copy_string(exten, lot->cfg->parkext, length); ast_copy_string(exten, lot->cfg->parkext, length);
} }
return parking_blind_transfer_park(parker, lot->cfg->parking_con, lot->cfg->parkext); return parking_blind_transfer_park(parker, lot->cfg->parking_con, lot->cfg->parkext, NULL, NULL);
} }
static int feature_park_call(struct ast_bridge_channel *bridge_channel, void *hook_pvt) static int feature_park_call(struct ast_bridge_channel *bridge_channel, void *hook_pvt)

@ -55,6 +55,8 @@ struct refer_progress {
struct ast_taskprocessor *serializer; struct ast_taskprocessor *serializer;
/*! \brief Stasis subscription for bridge events */ /*! \brief Stasis subscription for bridge events */
struct stasis_subscription *bridge_sub; struct stasis_subscription *bridge_sub;
/*! \brief Reference to transfer_channel_data related to the refer */
struct transfer_channel_data *transfer_data;
/*! \brief Uniqueid of transferee channel */ /*! \brief Uniqueid of transferee channel */
char *transferee; char *transferee;
}; };
@ -165,6 +167,12 @@ static void refer_progress_bridge(void *data, struct stasis_subscription *sub,
return; return;
} }
if (!progress->transfer_data->completed) {
/* We can't act on this message because the transfer_channel_data doesn't show that
* the transfer is ready to progress */
return;
}
/* OMG the transferee is joining a bridge. His call got answered! */ /* OMG the transferee is joining a bridge. His call got answered! */
notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED); notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED);
if (notification) { if (notification) {
@ -186,6 +194,11 @@ static struct ast_frame *refer_progress_framehook(struct ast_channel *chan, stru
return f; return f;
} }
/* If the completed flag hasn't been raised, skip this pass. */
if (!progress->transfer_data->completed) {
return f;
}
/* Determine the state of the REFER based on the control frames (or voice frames) passing */ /* Determine the state of the REFER based on the control frames (or voice frames) passing */
if (f->frametype == AST_FRAME_VOICE && !progress->subclass) { if (f->frametype == AST_FRAME_VOICE && !progress->subclass) {
/* Media is passing without progress, this means the call has been answered */ /* Media is passing without progress, this means the call has been answered */
@ -240,6 +253,10 @@ static void refer_progress_framehook_destroy(void *data)
ao2_cleanup(notification); ao2_cleanup(notification);
} }
if (progress->bridge_sub) {
progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
}
ao2_cleanup(progress); ao2_cleanup(progress);
} }
@ -296,6 +313,8 @@ static void refer_progress_destroy(void *obj)
progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub); progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub);
} }
ao2_cleanup(progress->transfer_data);
ast_free(progress->transferee); ast_free(progress->transferee);
ast_taskprocessor_unreference(progress->serializer); ast_taskprocessor_unreference(progress->serializer);
} }
@ -472,9 +491,10 @@ struct refer_blind {
}; };
/*! \brief Blind transfer callback function */ /*! \brief Blind transfer callback function */
static void refer_blind_callback(struct ast_channel *chan, void *user_data, enum ast_transfer_type transfer_type) static void refer_blind_callback(struct ast_channel *chan, struct transfer_channel_data *user_data_wrapper,
enum ast_transfer_type transfer_type)
{ {
struct refer_blind *refer = user_data; struct refer_blind *refer = user_data_wrapper->data;
pjsip_generic_string_hdr *referred_by; pjsip_generic_string_hdr *referred_by;
static const pj_str_t str_referred_by = { "Referred-By", 11 }; static const pj_str_t str_referred_by = { "Referred-By", 11 };
@ -503,6 +523,10 @@ static void refer_blind_callback(struct ast_channel *chan, void *user_data, enum
} }
} }
/* Progress needs a reference to the transfer_channel_data so that it can track the completed status of the transfer */
ao2_ref(user_data_wrapper, +1);
refer->progress->transfer_data = user_data_wrapper;
/* We need to bump the reference count up on the progress structure since it is in the frame hook now */ /* We need to bump the reference count up on the progress structure since it is in the frame hook now */
ao2_ref(refer->progress, +1); ao2_ref(refer->progress, +1);

Loading…
Cancel
Save