From a5b38b604c9ecec8cfb209439a94476834dbb7fc Mon Sep 17 00:00:00 2001 From: Kevin Harwell Date: Thu, 14 Jan 2016 14:42:57 -0600 Subject: bridge_basic: don't cache xferfailsound during an attended transfer The xferfailsound was read from the channel at the beginning of the transfer, and that value is "cached" for the duration of the transfer. Therefore, changing the xferfailsound on the channel using the FEATURE() dialplan function does nothing once the transfer is under way. This makes it so the transfer code instead gets the xferfailsound configuration options from the channel when it is actually going to be used. This patch also fixes a potential memory leak of the props object as well as making sure the condition variable gets initialized before being destroyed. ASTERISK-25696 #close Change-Id: Ic726b0f54ef588bd9c9c67f4b0e4d787934f85e4 --- include/asterisk/features_config.h | 15 +++++++ main/bridge_basic.c | 80 +++++++++++++++++++++++++++++--------- main/features_config.c | 15 +++++++ 3 files changed, 92 insertions(+), 18 deletions(-) diff --git a/include/asterisk/features_config.h b/include/asterisk/features_config.h index b15759ba6..baaff183b 100644 --- a/include/asterisk/features_config.h +++ b/include/asterisk/features_config.h @@ -101,6 +101,21 @@ struct ast_features_xfer_config { */ struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_channel *chan); +/*! + * \brief Get the transfer configuration option xferfailsound + * + * \note The channel should be locked before calling this function. + * \note The returned value has to be freed. + * + * If no channel is provided, then option is pulled from the global + * transfer configuration. + * + * \param chan The channel to get configuration options for + * \retval NULL Failed to get configuration + * \retval non-NULL The xferfailsound + */ +char *ast_get_chan_features_xferfailsound(struct ast_channel *chan); + /*! * \brief Configuration relating to call pickup */ diff --git a/main/bridge_basic.c b/main/bridge_basic.c index cd1991520..5d8ee49d0 100644 --- a/main/bridge_basic.c +++ b/main/bridge_basic.c @@ -1296,8 +1296,6 @@ struct attended_transfer_properties { AST_STRING_FIELD(exten); /*! Context of transfer target */ AST_STRING_FIELD(context); - /*! Sound to play on failure */ - AST_STRING_FIELD(failsound); /*! Sound to play when transfer completes */ AST_STRING_FIELD(xfersound); /*! The channel technology of the transferer channel */ @@ -1421,12 +1419,21 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc( struct ast_flags *transferer_features; props = ao2_alloc(sizeof(*props), attended_transfer_properties_destructor); - if (!props || ast_string_field_init(props, 64)) { + if (!props) { + ast_log(LOG_ERROR, "Unable to create props - channel %s, context %s\n", + ast_channel_name(transferer), context); return NULL; } ast_cond_init(&props->cond, NULL); + if (ast_string_field_init(props, 64)) { + ast_log(LOG_ERROR, "Unable to initialize prop fields - channel %s, context %s\n", + ast_channel_name(transferer), context); + ao2_ref(props, -1); + return NULL; + } + props->target_framehook_id = -1; props->transferer = ast_channel_ref(transferer); @@ -1447,7 +1454,6 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc( props->atxfernoanswertimeout = xfer_cfg->atxfernoanswertimeout; props->atxferloopdelay = xfer_cfg->atxferloopdelay; 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); @@ -1707,6 +1713,44 @@ static void play_sound(struct ast_channel *chan, const char *sound) } } +/*! + * \brief Helper method to play a fail sound on a channel in a bridge + * + * \param chan The channel to play the fail sound to + */ +static void play_failsound(struct ast_channel *chan) +{ + char *sound; + + ast_channel_lock(chan); + sound = ast_get_chan_features_xferfailsound(chan); + ast_channel_unlock(chan); + + if (sound) { + play_sound(chan, sound); + ast_free(sound); + } +} + +/*! + * \brief Helper method to stream a fail sound on a channel + * + * \param chan The channel to stream the fail sound to + */ +static void stream_failsound(struct ast_channel *chan) +{ + char *sound; + + ast_channel_lock(chan); + sound = ast_get_chan_features_xferfailsound(chan); + ast_channel_unlock(chan); + + if (sound) { + ast_stream_and_wait(chan, sound, AST_DIGIT_NONE); + ast_free(sound); + } +} + /*! * \brief Helper method to place a channel in a bridge on hold */ @@ -2049,7 +2093,7 @@ static enum attended_transfer_state calling_target_exit(struct attended_transfer { switch (stimulus) { case STIMULUS_TRANSFEREE_HANGUP: - play_sound(props->transferer, props->failsound); + play_failsound(props->transferer); publish_transfer_fail(props); return TRANSFER_FAIL; case STIMULUS_DTMF_ATXFER_COMPLETE: @@ -2061,7 +2105,7 @@ static enum attended_transfer_state calling_target_exit(struct attended_transfer case STIMULUS_TRANSFER_TARGET_HANGUP: case STIMULUS_TIMEOUT: case STIMULUS_DTMF_ATXFER_ABORT: - play_sound(props->transferer, props->failsound); + play_failsound(props->transferer); return TRANSFER_REBRIDGE; case STIMULUS_DTMF_ATXFER_THREEWAY: bridge_unhold(props->transferee_bridge); @@ -2090,7 +2134,7 @@ static enum attended_transfer_state hesitant_exit(struct attended_transfer_prope { switch (stimulus) { case STIMULUS_TRANSFEREE_HANGUP: - play_sound(props->transferer, props->failsound); + play_failsound(props->transferer); publish_transfer_fail(props); return TRANSFER_FAIL; case STIMULUS_DTMF_ATXFER_COMPLETE: @@ -2101,7 +2145,7 @@ static enum attended_transfer_state hesitant_exit(struct attended_transfer_prope case STIMULUS_TRANSFER_TARGET_HANGUP: case STIMULUS_TIMEOUT: case STIMULUS_DTMF_ATXFER_ABORT: - play_sound(props->transferer, props->failsound); + play_failsound(props->transferer); return TRANSFER_RESUME; case STIMULUS_DTMF_ATXFER_THREEWAY: return TRANSFER_THREEWAY; @@ -2163,7 +2207,7 @@ static enum attended_transfer_state consulting_exit(struct attended_transfer_pro * a sound to the transferer to indicate the transferee is gone. */ bridge_basic_change_personality(props->target_bridge, BRIDGE_BASIC_PERSONALITY_NORMAL, NULL); - play_sound(props->transferer, props->failsound); + play_failsound(props->transferer); ast_bridge_merge_inhibit(props->target_bridge, -1); /* These next two lines are here to ensure that our reference to the target bridge * is cleaned up properly and that the target bridge is not destroyed when the @@ -2179,7 +2223,7 @@ static enum attended_transfer_state consulting_exit(struct attended_transfer_pro return TRANSFER_COMPLETE; case STIMULUS_TRANSFER_TARGET_HANGUP: case STIMULUS_DTMF_ATXFER_ABORT: - play_sound(props->transferer, props->failsound); + play_failsound(props->transferer); return TRANSFER_REBRIDGE; case STIMULUS_DTMF_ATXFER_THREEWAY: bridge_unhold(props->transferee_bridge); @@ -2211,7 +2255,7 @@ static enum attended_transfer_state double_checking_exit(struct attended_transfe { switch (stimulus) { case STIMULUS_TRANSFEREE_HANGUP: - play_sound(props->transferer, props->failsound); + play_failsound(props->transferer); publish_transfer_fail(props); return TRANSFER_FAIL; case STIMULUS_TRANSFERER_HANGUP: @@ -2221,7 +2265,7 @@ static enum attended_transfer_state double_checking_exit(struct attended_transfe return TRANSFER_COMPLETE; case STIMULUS_TRANSFER_TARGET_HANGUP: case STIMULUS_DTMF_ATXFER_ABORT: - play_sound(props->transferer, props->failsound); + play_failsound(props->transferer); return TRANSFER_RESUME; case STIMULUS_DTMF_ATXFER_THREEWAY: bridge_unhold(props->target_bridge); @@ -3295,7 +3339,7 @@ static int feature_attended_transfer(struct ast_bridge_channel *bridge_channel, props->transfer_target = dial_transfer(bridge_channel->chan, destination); if (!props->transfer_target) { ast_log(LOG_ERROR, "Unable to request outbound channel for attended transfer target.\n"); - ast_stream_and_wait(props->transferer, props->failsound, AST_DIGIT_NONE); + stream_failsound(props->transferer); ast_bridge_channel_write_unhold(bridge_channel); attended_transfer_properties_shutdown(props); return 0; @@ -3306,7 +3350,7 @@ static int feature_attended_transfer(struct ast_bridge_channel *bridge_channel, props->target_bridge = ast_bridge_basic_new(); if (!props->target_bridge) { ast_log(LOG_ERROR, "Unable to create bridge for attended transfer target.\n"); - ast_stream_and_wait(props->transferer, props->failsound, AST_DIGIT_NONE); + stream_failsound(props->transferer); ast_bridge_channel_write_unhold(bridge_channel); ast_hangup(props->transfer_target); props->transfer_target = NULL; @@ -3317,7 +3361,7 @@ static int feature_attended_transfer(struct ast_bridge_channel *bridge_channel, if (attach_framehook(props, props->transfer_target)) { ast_log(LOG_ERROR, "Unable to attach framehook to transfer target.\n"); - ast_stream_and_wait(props->transferer, props->failsound, AST_DIGIT_NONE); + stream_failsound(props->transferer); ast_bridge_channel_write_unhold(bridge_channel); ast_hangup(props->transfer_target); props->transfer_target = NULL; @@ -3332,7 +3376,7 @@ static int feature_attended_transfer(struct ast_bridge_channel *bridge_channel, if (ast_call(props->transfer_target, destination, 0)) { ast_log(LOG_ERROR, "Unable to place outbound call to transfer target.\n"); - ast_stream_and_wait(bridge_channel->chan, props->failsound, AST_DIGIT_NONE); + stream_failsound(props->transferer); ast_bridge_channel_write_unhold(bridge_channel); ast_hangup(props->transfer_target); props->transfer_target = NULL; @@ -3348,7 +3392,7 @@ static int feature_attended_transfer(struct ast_bridge_channel *bridge_channel, if (ast_bridge_impart(props->target_bridge, props->transfer_target, NULL, NULL, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_log(LOG_ERROR, "Unable to place transfer target into bridge.\n"); - ast_stream_and_wait(bridge_channel->chan, props->failsound, AST_DIGIT_NONE); + stream_failsound(props->transferer); ast_bridge_channel_write_unhold(bridge_channel); ast_hangup(props->transfer_target); props->transfer_target = NULL; @@ -3358,7 +3402,7 @@ static int feature_attended_transfer(struct ast_bridge_channel *bridge_channel, if (ast_pthread_create_detached(&thread, NULL, attended_transfer_monitor_thread, props)) { ast_log(LOG_ERROR, "Unable to create monitoring thread for attended transfer.\n"); - ast_stream_and_wait(bridge_channel->chan, props->failsound, AST_DIGIT_NONE); + stream_failsound(props->transferer); ast_bridge_channel_write_unhold(bridge_channel); attended_transfer_properties_shutdown(props); return 0; diff --git a/main/features_config.c b/main/features_config.c index 3db1a780a..58ccb959b 100644 --- a/main/features_config.c +++ b/main/features_config.c @@ -1158,6 +1158,21 @@ struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_ch return cfg->global->xfer; } +char *ast_get_chan_features_xferfailsound(struct ast_channel *chan) +{ + char *res; + struct ast_features_xfer_config *cfg = ast_get_chan_features_xfer_config(chan); + + if (!cfg) { + return NULL; + } + + res = ast_strdup(cfg->xferfailsound); + ao2_ref(cfg, -1); + + return res; +} + struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan) { RAII_VAR(struct features_config *, cfg, NULL, ao2_cleanup); -- cgit v1.2.3