Fixup hold/unhold with attended and blind transfers.
authorRichard Mudgett <rmudgett@digium.com>
Fri, 31 May 2013 15:34:20 +0000 (15:34 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Fri, 31 May 2013 15:34:20 +0000 (15:34 +0000)
* 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
include/asterisk/bridging.h
include/asterisk/stasis_channels.h
main/bridging.c
main/channel.c
main/stasis_channels.c

index 0ae1e19..9574bb8 100644 (file)
@@ -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;
index e6d6623..b589874 100644 (file)
@@ -1110,6 +1110,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
  *
index 64224c0..d16b92d 100644 (file)
@@ -273,6 +273,19 @@ 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.
  *
index c437be3..d0a92d3 100644 (file)
@@ -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,
index 6549131..1902713 100644 (file)
@@ -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)
index ae253dd..14f2650 100644 (file)
@@ -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);