From ccc8cc5346f7f68f38b1749755777a33cd897f07 Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Fri, 31 May 2013 15:34:20 +0000 Subject: [PATCH] Fixup hold/unhold with attended and blind transfers. * DTMF attended and blind transfers have hold/unhold behavior restored. * External attended and blind transfers unhold the transfered party when the transfer is initiated. * Made prohibit blind transferring a bridge marked as masquerade only. (ConfBridge bridges) * Made running an application or playing a file inside a bridge post the hold/unhold messages if MOH is requested. Review: https://reviewboard.asterisk.org/r/2574/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390289 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- bridges/bridge_builtin_features.c | 15 +++- include/asterisk/bridging.h | 21 +++++ include/asterisk/stasis_channels.h | 13 +++ main/bridging.c | 131 ++++++++++++++++++----------- main/channel.c | 29 ++----- main/stasis_channels.c | 14 +++ 6 files changed, 149 insertions(+), 74 deletions(-) diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index 0ae1e19f7e..9574bb8445 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -182,7 +182,8 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c const char *context; char *goto_on_blindxfr; -/* BUGBUG the peer needs to be put on hold for the transfer. */ + ast_bridge_channel_write_hold(bridge_channel, NULL); + ast_channel_lock(bridge_channel->chan); context = ast_strdupa(get_transfer_context(bridge_channel->chan, blind_transfer ? blind_transfer->context : NULL)); @@ -192,6 +193,7 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c /* Grab the extension to transfer to */ if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) { + ast_bridge_channel_write_unhold(bridge_channel); return 0; } @@ -264,9 +266,10 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg const char *context; enum atxfer_code transfer_code = ATXFER_INCOMPLETE; + ast_bridge_channel_write_hold(bridge_channel, NULL); + bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1); -/* BUGBUG the peer needs to be put on hold for the transfer. */ ast_channel_lock(bridge_channel->chan); context = ast_strdupa(get_transfer_context(bridge_channel->chan, attended_transfer ? attended_transfer->context : NULL)); @@ -276,6 +279,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) { ast_bridge_merge_inhibit(bridge, -1); ao2_ref(bridge, -1); + ast_bridge_channel_write_unhold(bridge_channel); return 0; } @@ -286,6 +290,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg ao2_ref(bridge, -1); /* BUGBUG beeperr needs to be configurable from features.conf */ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); + ast_bridge_channel_write_unhold(bridge_channel); return 0; } @@ -313,6 +318,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg ao2_ref(bridge, -1); /* BUGBUG beeperr needs to be configurable from features.conf */ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); + ast_bridge_channel_write_unhold(bridge_channel); return 0; } @@ -326,6 +332,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg ao2_ref(bridge, -1); /* BUGBUG beeperr needs to be configurable from features.conf */ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); + ast_bridge_channel_write_unhold(bridge_channel); return 0; } ast_bridge_merge_inhibit(attended_bridge, +1); @@ -340,6 +347,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg ao2_ref(bridge, -1); /* BUGBUG beeperr needs to be configurable from features.conf */ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); + ast_bridge_channel_write_unhold(bridge_channel); return 0; } @@ -384,6 +392,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg break; case ATXFER_COMPLETE: /* The peer takes our place in the bridge. */ + ast_bridge_channel_write_unhold(bridge_channel); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, bridge_channel->chan, NULL, 1); break; @@ -394,6 +403,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg * Just impart the peer onto the bridge and have us return to it * as normal. */ + ast_bridge_channel_write_unhold(bridge_channel); xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, NULL, NULL, 1); break; case ATXFER_ABORT: @@ -407,6 +417,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg if (!ast_check_hangup_locked(bridge_channel->chan)) { ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE); } + ast_bridge_channel_write_unhold(bridge_channel); } return 0; diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h index e6d6623c2b..b589874b02 100644 --- a/include/asterisk/bridging.h +++ b/include/asterisk/bridging.h @@ -1109,6 +1109,27 @@ void ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_cha */ void ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen); +/*! + * \brief Write a hold frame into the bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the hold into the bridge. + * \param moh_class The suggested music class for the other end to use. + * + * \return Nothing + */ +void ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class); + +/*! + * \brief Write an unhold frame into the bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the hold into the bridge. + * + * \return Nothing + */ +void ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel); + /*! * \brief Run an application on the bridge channel. * \since 12.0.0 diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h index 64224c04f0..d16b92dc2f 100644 --- a/include/asterisk/stasis_channels.h +++ b/include/asterisk/stasis_channels.h @@ -272,6 +272,19 @@ struct ast_json *ast_multi_channel_blob_get_json(struct ast_multi_channel_blob * void ast_multi_channel_blob_add_channel(struct ast_multi_channel_blob *obj, const char *role, struct ast_channel_snapshot *snapshot); +/*! + * \brief Publish a channel blob message. + * \since 12.0.0 + * + * \param chan Channel publishing the blob. + * \param type Type of stasis message. + * \param blob The blob being published. (NULL if no blob) + * + * \return Nothing + */ +void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type, + struct ast_json *blob); + /*! * \since 12 * \brief Publish a \ref ast_channel_snapshot for a channel. diff --git a/main/bridging.c b/main/bridging.c index c437be3359..d0a92d3d99 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -43,6 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/bridging_basic.h" #include "asterisk/bridging_technology.h" #include "asterisk/stasis_bridging.h" +#include "asterisk/stasis_channels.h" #include "asterisk/app.h" #include "asterisk/file.h" #include "asterisk/module.h" @@ -714,6 +715,32 @@ void ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_cha bridge_channel_write_frame(bridge_channel, &frame); } +void ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class) +{ + RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); + size_t datalen; + + if (!ast_strlen_zero(moh_class)) { + datalen = strlen(moh_class) + 1; + + blob = ast_json_pack("{s: s}", + "musicclass", moh_class); + } else { + moh_class = NULL; + datalen = 0; + } + + ast_channel_publish_blob(bridge_channel->chan, ast_channel_hold_type(), blob); + ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD, moh_class, + datalen); +} + +void ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel) +{ + ast_channel_publish_blob(bridge_channel->chan, ast_channel_unhold_type(), NULL); + ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0); +} + static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args) { int res = 0; @@ -738,21 +765,14 @@ static int run_app_helper(struct ast_channel *chan, const char *app_name, const void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class) { if (moh_class) { - if (ast_strlen_zero(moh_class)) { - ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD, - NULL, 0); - } else { - ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD, - moh_class, strlen(moh_class) + 1); - } + ast_bridge_channel_write_hold(bridge_channel, moh_class); } if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) { /* Break the bridge if the app returns non-zero. */ bridge_handle_hangup(bridge_channel); } if (moh_class) { - ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, - NULL, 0); + ast_bridge_channel_write_unhold(bridge_channel); } } @@ -821,13 +841,7 @@ void ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, con void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class) { if (moh_class) { - if (ast_strlen_zero(moh_class)) { - ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD, - NULL, 0); - } else { - ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD, - moh_class, strlen(moh_class) + 1); - } + ast_bridge_channel_write_hold(bridge_channel, moh_class); } if (custom_play) { custom_play(bridge_channel, playfile); @@ -835,8 +849,7 @@ void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_ ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE); } if (moh_class) { - ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, - NULL, 0); + ast_bridge_channel_write_unhold(bridge_channel); } /* @@ -5428,25 +5441,49 @@ static void set_blind_transfer_variables(struct ast_channel *transferer, struct ao2_iterator_destroy(&iter); } +static struct ast_bridge *acquire_bridge(struct ast_channel *chan) +{ + struct ast_bridge *bridge; + + ast_channel_lock(chan); + bridge = ast_channel_get_bridge(chan); + ast_channel_unlock(chan); + + if (bridge + && ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) { + ao2_ref(bridge, -1); + bridge = NULL; + } + + return bridge; +} + enum ast_transfer_result ast_bridge_transfer_blind(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); + RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup); int do_bridge_transfer; int transfer_prohibited; enum try_parking_result parking_result; + bridge = acquire_bridge(transferer); + if (!bridge) { + return AST_BRIDGE_TRANSFER_INVALID; + } ast_channel_lock(transferer); - bridge = ast_channel_get_bridge(transferer); + bridge_channel = ast_channel_get_bridge_channel(transferer); ast_channel_unlock(transferer); - - if (!bridge) { + if (!bridge_channel) { return AST_BRIDGE_TRANSFER_INVALID; } + /* Take off hold if they are on hold. */ + ast_bridge_channel_write_unhold(bridge_channel); + parking_result = try_parking(bridge, transferer, exten, context); switch (parking_result) { case PARKING_SUCCESS: @@ -5501,26 +5538,6 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere return AST_BRIDGE_TRANSFER_SUCCESS; } -static struct ast_bridge *acquire_bridge(struct ast_channel *chan) -{ - struct ast_bridge *bridge; - - ast_channel_lock(chan); - bridge = ast_channel_get_bridge(chan); - ast_channel_unlock(chan); - - if (!bridge) { - return NULL; - } - - if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) { - ao2_ref(bridge, -1); - bridge = NULL; - } - - return bridge; -} - /*! * \internal * \brief Performs an attended transfer by moving a channel from one bridge to another @@ -5613,6 +5630,8 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra { RAII_VAR(struct ast_bridge *, to_transferee_bridge, NULL, ao2_cleanup); RAII_VAR(struct ast_bridge *, to_target_bridge, NULL, ao2_cleanup); + RAII_VAR(struct ast_bridge_channel *, to_transferee_bridge_channel, NULL, ao2_cleanup); + RAII_VAR(struct ast_bridge_channel *, to_target_bridge_channel, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup); struct ast_bridge *the_bridge; @@ -5629,19 +5648,31 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra return AST_BRIDGE_TRANSFER_INVALID; } + ast_channel_lock(to_transferee); + to_transferee_bridge_channel = ast_channel_get_bridge_channel(to_transferee); + ast_channel_unlock(to_transferee); + + ast_channel_lock(to_transfer_target); + to_target_bridge_channel = ast_channel_get_bridge_channel(to_transfer_target); + ast_channel_unlock(to_transfer_target); + + if (to_transferee_bridge_channel) { + /* Take off hold if they are on hold. */ + ast_bridge_channel_write_unhold(to_transferee_bridge_channel); + } + + if (to_target_bridge_channel) { + /* Take off hold if they are on hold. */ + ast_bridge_channel_write_unhold(to_target_bridge_channel); + } + /* Let's get the easy one out of the way first */ if (to_transferee_bridge && to_target_bridge) { - RAII_VAR(struct ast_bridge_channel *, to_transferee_bridge_channel, NULL, ao2_cleanup); - RAII_VAR(struct ast_bridge_channel *, to_target_bridge_channel, NULL, ao2_cleanup); enum ast_transfer_result res; - ast_channel_lock(to_transferee); - to_transferee_bridge_channel = ast_channel_get_bridge_channel(to_transferee); - ast_channel_unlock(to_transferee); - - ast_channel_lock(to_transfer_target); - to_target_bridge_channel = ast_channel_get_bridge_channel(to_transfer_target); - ast_channel_unlock(to_transfer_target); + if (!to_transferee_bridge_channel || !to_target_bridge_channel) { + return AST_BRIDGE_TRANSFER_INVALID; + } ast_bridge_lock_both(to_transferee_bridge, to_target_bridge); res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel, diff --git a/main/channel.c b/main/channel.c index 6549131bff..190271321e 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1348,21 +1348,6 @@ int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *fin) return __ast_queue_frame(chan, fin, 1, NULL); } -/*! \internal \brief Publish a channel blob message */ -static void publish_channel_blob(struct ast_channel *chan, - struct stasis_message_type *type, struct ast_json *blob) -{ - RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); - if (!blob) { - blob = ast_json_null(); - } - - message = ast_channel_blob_create(chan, type, blob); - if (message) { - stasis_publish(ast_channel_topic(chan), message); - } -} - /*! \brief Queue a hangup frame for channel */ int ast_queue_hangup(struct ast_channel *chan) { @@ -1372,7 +1357,7 @@ int ast_queue_hangup(struct ast_channel *chan) /* Yeah, let's not change a lock-critical value without locking */ ast_channel_lock(chan); ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_DEV); - publish_channel_blob(chan, ast_channel_hangup_request_type(), NULL); + ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), NULL); res = ast_queue_frame(chan, &f); ast_channel_unlock(chan); @@ -1398,7 +1383,7 @@ int ast_queue_hangup_with_cause(struct ast_channel *chan, int cause) } blob = ast_json_pack("{s: i}", "cause", cause); - publish_channel_blob(chan, ast_channel_hangup_request_type(), blob); + ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), blob); res = ast_queue_frame(chan, &f); ast_channel_unlock(chan); @@ -1419,7 +1404,7 @@ int ast_queue_hold(struct ast_channel *chan, const char *musicclass) "musicclass", musicclass); } - publish_channel_blob(chan, ast_channel_hold_type(), blob); + ast_channel_publish_blob(chan, ast_channel_hold_type(), blob); res = ast_queue_frame(chan, &f); return res; @@ -1430,7 +1415,7 @@ int ast_queue_unhold(struct ast_channel *chan) struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_UNHOLD }; int res; - publish_channel_blob(chan, ast_channel_unhold_type(), NULL); + ast_channel_publish_blob(chan, ast_channel_unhold_type(), NULL); res = ast_queue_frame(chan, &f); return res; @@ -2741,7 +2726,7 @@ int ast_softhangup(struct ast_channel *chan, int cause) blob = ast_json_pack("{s: i, s: b}", "cause", cause, "soft", 1); - publish_channel_blob(chan, ast_channel_hangup_request_type(), blob); + ast_channel_publish_blob(chan, ast_channel_hangup_request_type(), blob); ast_channel_unlock(chan); return res; @@ -3755,7 +3740,7 @@ static void send_dtmf_begin_event(struct ast_channel *chan, return; } - publish_channel_blob(chan, ast_channel_dtmf_begin_type(), blob); + ast_channel_publish_blob(chan, ast_channel_dtmf_begin_type(), blob); } static void send_dtmf_end_event(struct ast_channel *chan, @@ -3772,7 +3757,7 @@ static void send_dtmf_end_event(struct ast_channel *chan, return; } - publish_channel_blob(chan, ast_channel_dtmf_end_type(), blob); + ast_channel_publish_blob(chan, ast_channel_dtmf_end_type(), blob); } static void ast_read_generator_actions(struct ast_channel *chan, struct ast_frame *f) diff --git a/main/stasis_channels.c b/main/stasis_channels.c index ae253ddca8..14f265018a 100644 --- a/main/stasis_channels.c +++ b/main/stasis_channels.c @@ -484,6 +484,20 @@ struct ast_json *ast_multi_channel_blob_get_json(struct ast_multi_channel_blob * return obj->blob; } +void ast_channel_publish_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob) +{ + RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); + + if (!blob) { + blob = ast_json_null(); + } + + message = ast_channel_blob_create(chan, type, blob); + if (message) { + stasis_publish(ast_channel_topic(chan), message); + } +} + void ast_channel_publish_snapshot(struct ast_channel *chan) { RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);