|
|
|
@ -606,18 +606,17 @@ static int setup_dynamic_feature(void *obj, void *arg, void *data, int flags)
|
|
|
|
|
*/
|
|
|
|
|
static int setup_bridge_features_dynamic(struct ast_bridge_features *features, struct ast_channel *chan)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct ao2_container *, applicationmap, NULL, ao2_cleanup);
|
|
|
|
|
struct ao2_container *applicationmap;
|
|
|
|
|
int res = 0;
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
|
applicationmap = ast_get_chan_applicationmap(chan);
|
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
|
if (!applicationmap) {
|
|
|
|
|
return 0;
|
|
|
|
|
if (applicationmap) {
|
|
|
|
|
ao2_callback_data(applicationmap, 0, setup_dynamic_feature, features, &res);
|
|
|
|
|
ao2_ref(applicationmap, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_callback_data(applicationmap, 0, setup_dynamic_feature, features, &res);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1418,7 +1417,7 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc(
|
|
|
|
|
char *tech;
|
|
|
|
|
char *addr;
|
|
|
|
|
char *serial;
|
|
|
|
|
RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_features_xfer_config *xfer_cfg;
|
|
|
|
|
struct ast_flags *transferer_features;
|
|
|
|
|
|
|
|
|
|
props = ao2_alloc(sizeof(*props), attended_transfer_properties_destructor);
|
|
|
|
@ -1450,6 +1449,7 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc(
|
|
|
|
|
ast_string_field_set(props, context, get_transfer_context(transferer, context));
|
|
|
|
|
ast_string_field_set(props, failsound, xfer_cfg->xferfailsound);
|
|
|
|
|
ast_string_field_set(props, xfersound, xfer_cfg->xfersound);
|
|
|
|
|
ao2_ref(xfer_cfg, -1);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Save the transferee's party information for any recall calls.
|
|
|
|
@ -1695,17 +1695,16 @@ static void publish_transfer_fail(struct attended_transfer_properties *props)
|
|
|
|
|
*/
|
|
|
|
|
static void play_sound(struct ast_channel *chan, const char *sound)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_bridge_channel *bridge_channel;
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
|
bridge_channel = ast_channel_get_bridge_channel(chan);
|
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
|
|
|
|
|
|
if (!bridge_channel) {
|
|
|
|
|
return;
|
|
|
|
|
if (bridge_channel) {
|
|
|
|
|
ast_bridge_channel_queue_playfile(bridge_channel, NULL, sound, NULL);
|
|
|
|
|
ao2_ref(bridge_channel, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_bridge_channel_queue_playfile(bridge_channel, NULL, sound, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
@ -1713,16 +1712,19 @@ static void play_sound(struct ast_channel *chan, const char *sound)
|
|
|
|
|
*/
|
|
|
|
|
static void hold(struct ast_channel *chan)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_bridge_channel *bridge_channel;
|
|
|
|
|
|
|
|
|
|
if (chan) {
|
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
|
bridge_channel = ast_channel_get_bridge_channel(chan);
|
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
|
if (!chan) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_assert(bridge_channel != NULL);
|
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
|
bridge_channel = ast_channel_get_bridge_channel(chan);
|
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
|
|
|
|
|
|
if (bridge_channel) {
|
|
|
|
|
ast_bridge_channel_write_hold(bridge_channel, NULL);
|
|
|
|
|
ao2_ref(bridge_channel, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1731,15 +1733,20 @@ static void hold(struct ast_channel *chan)
|
|
|
|
|
*/
|
|
|
|
|
static void unhold(struct ast_channel *chan)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_bridge_channel *bridge_channel;
|
|
|
|
|
|
|
|
|
|
if (!chan) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
|
bridge_channel = ast_channel_get_bridge_channel(chan);
|
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
|
|
|
|
|
|
ast_assert(bridge_channel != NULL);
|
|
|
|
|
|
|
|
|
|
ast_bridge_channel_write_unhold(bridge_channel);
|
|
|
|
|
if (bridge_channel) {
|
|
|
|
|
ast_bridge_channel_write_unhold(bridge_channel);
|
|
|
|
|
ao2_ref(bridge_channel, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
@ -1747,15 +1754,16 @@ static void unhold(struct ast_channel *chan)
|
|
|
|
|
*/
|
|
|
|
|
static void ringing(struct ast_channel *chan)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_bridge_channel *bridge_channel;
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(chan);
|
|
|
|
|
bridge_channel = ast_channel_get_bridge_channel(chan);
|
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
|
|
|
|
|
|
ast_assert(bridge_channel != NULL);
|
|
|
|
|
|
|
|
|
|
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_RINGING, NULL, 0);
|
|
|
|
|
if (bridge_channel) {
|
|
|
|
|
ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_RINGING, NULL, 0);
|
|
|
|
|
ao2_ref(bridge_channel, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
@ -1800,10 +1808,9 @@ static void bridge_unhold(struct ast_bridge *bridge)
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Wrapper for \ref bridge_do_move
|
|
|
|
|
*/
|
|
|
|
|
static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel *channel, struct ast_channel *swap)
|
|
|
|
|
static void bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel *channel, struct ast_channel *swap)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_bridge_channel *bridge_channel;
|
|
|
|
|
|
|
|
|
|
ast_bridge_lock_both(src, dest);
|
|
|
|
|
|
|
|
|
@ -1811,18 +1818,18 @@ static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct a
|
|
|
|
|
bridge_channel = ast_channel_get_bridge_channel(channel);
|
|
|
|
|
ast_channel_unlock(channel);
|
|
|
|
|
|
|
|
|
|
ast_assert(bridge_channel != NULL);
|
|
|
|
|
|
|
|
|
|
ao2_lock(bridge_channel);
|
|
|
|
|
bridge_channel->swap = swap;
|
|
|
|
|
ao2_unlock(bridge_channel);
|
|
|
|
|
if (bridge_channel) {
|
|
|
|
|
ao2_lock(bridge_channel);
|
|
|
|
|
bridge_channel->swap = swap;
|
|
|
|
|
ao2_unlock(bridge_channel);
|
|
|
|
|
|
|
|
|
|
res = bridge_do_move(dest, bridge_channel, 1, 0);
|
|
|
|
|
bridge_do_move(dest, bridge_channel, 1, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_bridge_unlock(dest);
|
|
|
|
|
ast_bridge_unlock(src);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
ao2_cleanup(bridge_channel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
@ -2033,7 +2040,8 @@ static const struct attended_transfer_state_properties state_properties[] = {
|
|
|
|
|
|
|
|
|
|
static int calling_target_enter(struct attended_transfer_properties *props)
|
|
|
|
|
{
|
|
|
|
|
return bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL);
|
|
|
|
|
bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum attended_transfer_state calling_target_exit(struct attended_transfer_properties *props,
|
|
|
|
@ -2072,10 +2080,7 @@ static enum attended_transfer_state calling_target_exit(struct attended_transfer
|
|
|
|
|
|
|
|
|
|
static int hesitant_enter(struct attended_transfer_properties *props)
|
|
|
|
|
{
|
|
|
|
|
if (bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL);
|
|
|
|
|
unhold(props->transferer);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -2115,11 +2120,7 @@ static enum attended_transfer_state hesitant_exit(struct attended_transfer_prope
|
|
|
|
|
|
|
|
|
|
static int rebridge_enter(struct attended_transfer_properties *props)
|
|
|
|
|
{
|
|
|
|
|
if (bridge_move(props->transferee_bridge, props->target_bridge,
|
|
|
|
|
props->transferer, NULL)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL);
|
|
|
|
|
unhold(props->transferer);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -2840,7 +2841,8 @@ static void transfer_pull(struct ast_bridge *self, struct ast_bridge_channel *br
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self->num_channels == 1) {
|
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_bridge_channel *transferer_bridge_channel;
|
|
|
|
|
int not_transferer;
|
|
|
|
|
|
|
|
|
|
ast_channel_lock(props->transferer);
|
|
|
|
|
transferer_bridge_channel = ast_channel_get_bridge_channel(props->transferer);
|
|
|
|
@ -2850,7 +2852,9 @@ static void transfer_pull(struct ast_bridge *self, struct ast_bridge_channel *br
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AST_LIST_FIRST(&self->channels) != transferer_bridge_channel) {
|
|
|
|
|
not_transferer = AST_LIST_FIRST(&self->channels) != transferer_bridge_channel;
|
|
|
|
|
ao2_ref(transferer_bridge_channel, -1);
|
|
|
|
|
if (not_transferer) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -2887,7 +2891,8 @@ static void recall_pull(struct ast_bridge *self, struct ast_bridge_channel *brid
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self->num_channels == 1) {
|
|
|
|
|
RAII_VAR(struct ast_bridge_channel *, target_bridge_channel, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_bridge_channel *target_bridge_channel;
|
|
|
|
|
|
|
|
|
|
if (!props->recall_target) {
|
|
|
|
|
/* No recall target means that the pull happened on a transferee. If there's still
|
|
|
|
|
* a channel left in the bridge, we don't need to send a stimulus
|
|
|
|
@ -2899,12 +2904,11 @@ static void recall_pull(struct ast_bridge *self, struct ast_bridge_channel *brid
|
|
|
|
|
target_bridge_channel = ast_channel_get_bridge_channel(props->recall_target);
|
|
|
|
|
ast_channel_unlock(props->recall_target);
|
|
|
|
|
|
|
|
|
|
if (!target_bridge_channel) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AST_LIST_FIRST(&self->channels) == target_bridge_channel) {
|
|
|
|
|
stimulate_attended_transfer(props, STIMULUS_TRANSFEREE_HANGUP);
|
|
|
|
|
if (target_bridge_channel) {
|
|
|
|
|
if (AST_LIST_FIRST(&self->channels) == target_bridge_channel) {
|
|
|
|
|
stimulate_attended_transfer(props, STIMULUS_TRANSFEREE_HANGUP);
|
|
|
|
|
}
|
|
|
|
|
ao2_ref(target_bridge_channel, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -2926,7 +2930,8 @@ static void bridge_personality_atxfer_pull(struct ast_bridge *self, struct ast_b
|
|
|
|
|
|
|
|
|
|
static enum attended_transfer_stimulus wait_for_stimulus(struct attended_transfer_properties *props)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct stimulus_list *, list, NULL, ast_free_ptr);
|
|
|
|
|
enum attended_transfer_stimulus stimulus;
|
|
|
|
|
struct stimulus_list *list;
|
|
|
|
|
SCOPED_MUTEX(lock, ao2_object_get_lockaddr(props));
|
|
|
|
|
|
|
|
|
|
while (!(list = AST_LIST_REMOVE_HEAD(&props->stimulus_queue, next))) {
|
|
|
|
@ -2957,7 +2962,9 @@ static enum attended_transfer_stimulus wait_for_stimulus(struct attended_transfe
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return list->stimulus;
|
|
|
|
|
stimulus = list->stimulus;
|
|
|
|
|
ast_free(list);
|
|
|
|
|
return stimulus;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
@ -3052,7 +3059,7 @@ static int add_transferer_role(struct ast_channel *chan, struct ast_bridge_featu
|
|
|
|
|
const char *atxfer_threeway;
|
|
|
|
|
const char *atxfer_complete;
|
|
|
|
|
const char *atxfer_swap;
|
|
|
|
|
RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_features_xfer_config *xfer_cfg;
|
|
|
|
|
SCOPED_CHANNELLOCK(lock, chan);
|
|
|
|
|
|
|
|
|
|
xfer_cfg = ast_get_chan_features_xfer_config(chan);
|
|
|
|
@ -3070,6 +3077,7 @@ static int add_transferer_role(struct ast_channel *chan, struct ast_bridge_featu
|
|
|
|
|
atxfer_complete = ast_strdupa(xfer_cfg->atxfercomplete);
|
|
|
|
|
atxfer_swap = ast_strdupa(xfer_cfg->atxferswap);
|
|
|
|
|
}
|
|
|
|
|
ao2_ref(xfer_cfg, -1);
|
|
|
|
|
|
|
|
|
|
return ast_channel_add_bridge_role(chan, AST_TRANSFERER_ROLE_NAME) ||
|
|
|
|
|
ast_channel_set_bridge_role_option(chan, AST_TRANSFERER_ROLE_NAME, "abort", atxfer_abort) ||
|
|
|
|
@ -3090,7 +3098,7 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
|
|
|
|
|
int digit_timeout;
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
int max_attempts;
|
|
|
|
|
RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
|
|
|
|
|
struct ast_features_xfer_config *xfer_cfg;
|
|
|
|
|
char *retry_sound;
|
|
|
|
|
char *invalid_sound;
|
|
|
|
|
|
|
|
|
@ -3105,6 +3113,7 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
|
|
|
|
|
max_attempts = xfer_cfg->transferdialattempts;
|
|
|
|
|
retry_sound = ast_strdupa(xfer_cfg->transferretrysound);
|
|
|
|
|
invalid_sound = ast_strdupa(xfer_cfg->transferinvalidsound);
|
|
|
|
|
ao2_ref(xfer_cfg, -1);
|
|
|
|
|
ast_channel_unlock(chan);
|
|
|
|
|
|
|
|
|
|
/* Play the simple "transfer" prompt out and wait */
|
|
|
|
|