Fix race condition that could result in ARI transfer messages not being sent.
authorMark Michelson <mmichelson@digium.com>
Fri, 14 Nov 2014 15:28:42 +0000 (15:28 +0000)
committerMark Michelson <mmichelson@digium.com>
Fri, 14 Nov 2014 15:28:42 +0000 (15:28 +0000)
From reviewboard:

"During blind transfer testing, it was noticed that tests were failing
occasionally because the ARI blind transfer event was not being sent.
After investigating, I detected a race condition in the blind transfer
code. When blind transferring a single channel, the actual transfer
operation (i.e. removing the transferee from the bridge and directing
them to the proper dialplan location) is queued onto the transferee
bridge channel. After queuing the transfer operation, the blind transfer
Stasis message is published. At the time of publication, snapshots of
the channels and bridge involved are created. The ARI subscriber to the
blind transfer Stasis message then attempts to determine if the bridge
or any of the involved channels are subscribed to by ARI applications.
If so, then the blind transfer message is sent to the applications. The
way that the ARI blind transfer message handler works is to first see
if the transferer channel is subscribed to. If not, then iterate over
all the channel IDs in the bridge snapshot and determine if any of
those are subscribed to. In the test we were running, the lone
transferee channel was subscribed to, so an ARI event should have been
sent to our application. Occasionally, though, the bridge snapshot did
not have any channels IDs on it at all. Why?

The problem is that since the blind transfer operation is handled by a
separate thread, it is possible that the transfer will have completed and
the channels removed from the bridge before we publish the blind transfer
Stasis message. Since the blind transfer has completed, the bridge on
which the transfer occurred no longer has any channels on it, so the
resulting bridge snapshot has no channels on it. Through investigation of
the code, I found that attended transfers can have this issue too for the
case where a transferee is transferred to an application."

The fix employed here is to decouple the creation of snapshots for the transfer
messages from the publication of the transfer messages. This way, snapshots
can be created to reflect what they are at the time of the transfer operation.

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

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

Merged revisions 427870 from http://svn.asterisk.org/svn/asterisk/branches/13

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427873 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_queue.c
include/asterisk/stasis_bridges.h
main/bridge.c
main/bridge_basic.c
main/cel.c
main/stasis_bridges.c
res/stasis/app.c
tests/test_cel.c

index 8d1653c..4820c66 100644 (file)
@@ -5772,7 +5772,7 @@ static void handle_blind_transfer(void *userdata, struct stasis_subscription *su
        ao2_lock(queue_data);
 
        if (ast_strlen_zero(queue_data->bridge_uniqueid) ||
-                       strcmp(queue_data->bridge_uniqueid, transfer_msg->to_transferee.bridge_snapshot->uniqueid)) {
+                       strcmp(queue_data->bridge_uniqueid, transfer_msg->bridge->uniqueid)) {
                ao2_unlock(queue_data);
                return;
        }
index 699e276..d549e46 100644 (file)
@@ -269,14 +269,6 @@ struct ast_bridge_channel_snapshot_pair {
 };
 
 /*!
- * \brief Pair showing a bridge and a specific channel belonging to the bridge
- */
-struct ast_bridge_channel_pair {
-       struct ast_bridge *bridge;
-       struct ast_channel *channel;
-};
-
-/*!
  * \since 12
  * \brief Message type for \ref ast_blind_transfer_message.
  *
@@ -292,8 +284,10 @@ struct ast_blind_transfer_message {
        enum ast_transfer_result result;
        /*! True if the transfer was initiated by an external source (i.e. not DTMF-initiated) */
        int is_external;
-       /*! Transferer and its bridge */
-       struct ast_bridge_channel_snapshot_pair to_transferee;
+       /*! The transferring channel */
+       struct ast_channel_snapshot *transferer;
+       /*! The bridge between the transferer and the transferee */
+       struct ast_bridge_snapshot *bridge;
        /*! Destination context */
        char context[AST_MAX_CONTEXT];
        /*! Destination extension */
@@ -305,6 +299,21 @@ struct ast_blind_transfer_message {
 };
 
 /*!
+ * \brief Create a blind transfer message to be published
+ *
+ * \param is_external Whether the blind transfer was initiated externally (e.g. via AMI or native protocol)
+ * \param transferer The transferer's channel that is bridged to the transferee
+ * \param bridge The bridge the transferer and transferee are in
+ * \param context The destination context for the blind transfer
+ * \param exten The destination extension for the blind transfer
+ *
+ * \retval NULL Failure to allocate or create snapshots
+ * \retval non-NULL The created blind transfer message
+ */
+struct ast_blind_transfer_message *ast_blind_transfer_message_create(int is_external,
+               struct ast_channel *transferer, const char *exten, const char *context);
+
+/*!
  * \brief Publish a blind transfer event
  *
  * \pre Bridges involved are locked. Channels involved are not locked.
@@ -320,9 +329,7 @@ struct ast_blind_transfer_message {
  *                        cannot reach across the bridge due to bridge flags, this is
  *                        the channel connecting their bridge to the destination.
  */
-void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *to_transferee, const char *context, const char *exten,
-               struct ast_channel *transferee_channel, struct ast_channel *replace_channel);
+void ast_bridge_publish_blind_transfer(struct ast_blind_transfer_message *transfer_message);
 
 enum ast_attended_transfer_dest_type {
        /*! The transfer failed, so there is no appropriate final state */
@@ -372,145 +379,111 @@ struct ast_attended_transfer_message {
 };
 
 /*!
- * \since 12
- * \brief Message type for \ref ast_attended_transfer_message.
+ * \brief Create an Attended transfer message to be published.
  *
- * \retval Message type for \ref ast_attended_transfer_message.
+ * The parameters to this function are the basic necessities in order to create the
+ * initial attended transfer message.
+ *
+ * The transferee and transfer_target parameters are optional. If not provided, then this
+ * function will attempt to determine who the transferee and transfer target are based on
+ * the input transferer channels and bridges. You typically will not need to provide an
+ * explicit transferee and transfer target channel unless your attended transfer is implemented
+ * in a strange way.
+ *
+ * \param is_external Non-zero if the transfer was initiated by a native channel driver protocol.
+ * \param to_transferee The transferer channel that is bridged to the transferee channel.
+ * \param transferee_bridge The bridge between the transferer and transferee. May be NULL.
+ * \param to_transfer_target The transferer channel that is bridged to the transfer target.
+ * \param target_bridge The bridge between the transferer and transfer target. May be NULL.
+ * \param transferee The channel that is being transferred. Optional.
+ * \param transfer_target The channel that is being transferred to. Optional.
+ *
+ * \retval NULL Failure to allocate or create snapshots
+ * \retval non-NULL The created attended transfer message
  */
-struct stasis_message_type *ast_attended_transfer_type(void);
+struct ast_attended_transfer_message *ast_attended_transfer_message_create(
+               int is_external, struct ast_channel *to_transferee, struct ast_bridge *transferee_bridge,
+               struct ast_channel *to_transfer_target, struct ast_bridge *target_bridge,
+               struct ast_channel *transferee, struct ast_channel *transfer_target);
 
 /*!
- * \since 12
- * \brief Publish an attended transfer failure
+ * \brief Add details for a bridge merge to an attended transfer message.
  *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_FAIL.
+ * If the transfer is accomplished by a bridge merge (or swap optimization), then this should
+ * be called on the created attended transfer message to have the appropriate details added on.
  *
- * \pre Bridges involved are locked. Channels involved are not locked.
+ * \param transfer_msg The transfer message to add details to
+ * \param final_bridge The bridge where the surviving parties reside
  *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer. Will always be a type of failure.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
+ * \retval 0 Success
+ * \retval -1 Failure
  */
-void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_channel *transferee_channel, struct ast_channel *target_channel);
+int ast_attended_transfer_message_add_merge(struct ast_attended_transfer_message *transfer_msg,
+               struct ast_bridge *final_bridge);
 
 /*!
- * \since 12
- * \brief Publish an attended transfer that results in two bridges becoming one.
- *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE. This type of attended transfer results from
- * having two bridges involved and either
+ * \brief Add details for an attended transfer that was resolved as a three-way call
  *
- * \li Merging the two bridges together
- * \li Moving a channel from one bridge to the other, thus emptying a bridge
+ * If the transfer results in a three-way call between the transferer, the transferee, and the
+ * transfer target, then this should be called in order to add appropriate details to the
+ * transfer message to be published.
  *
- * In either case, two bridges enter, one leaves.
+ * \param transfer_msg The message to add details to
+ * \param survivor_channel The transferer channel that exists in the three-way call
+ * \param survivor_bridge The bridge where the three-way call takes place.
  *
- * \pre Bridges involved are locked. Channels involved are not locked.
- *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param final_bridge The bridge that the parties end up in. Will be a bridge from the transferee or target pair.
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
+ * \retval 0 Success
+ * \retval -1 Failure
  */
-void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_bridge *final_bridge, struct ast_channel *transferee_channel,
-               struct ast_channel *target_channel);
+int ast_attended_transfer_message_add_threeway(struct ast_attended_transfer_message *transfer_msg,
+               struct ast_channel *survivor_channel, struct ast_bridge *survivor_bridge);
 
 /*!
- * \since 12
- * \brief Publish an attended transfer that results in a threeway call.
+ * \brief Add details for an attended transfer to an application
  *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_THREEWAY. Like with \ref ast_bridge_publish_attended_transfer_bridge_merge,
- * this results from merging two bridges together. The difference is that a
- * transferer channel survives the bridge merge
+ * If the transfer is sending one or more parties into an application, then this should be called
+ * to add appropriate details to the transfer message being published.
  *
- * \pre Bridges involved are locked. Channels involved are not locked.
+ * \param transfer_msg The message to add details to
+ * \param app The name of the application that the parties are being transferred to
+ * \param replace_channel The local channel that is in the bridge and running the application
  *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param final_pair The bridge that the parties end up in, and the transferer channel that is in this bridge.
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
+ * \retval 0 Success
+ * \retval -1 Failure
  */
-void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_bridge_channel_pair *final_pair, struct ast_channel *transferee_channel,
-               struct ast_channel *target_channel);
+int ast_attended_transfer_message_add_app(struct ast_attended_transfer_message *transfer_msg,
+               const char *app, struct ast_channel *replace_channel);
 
 /*!
- * \since 12
- * \brief Publish an attended transfer that results in an application being run
+ * \brief Add details for an attended transfer that has a link between bridges.
  *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_APP. This occurs when an attended transfer
- * results in either:
+ * An attended transfer may be accomplished by linking two bridges together with local channels.
+ * If this is how the transfer is to be completed, call this function in order to fill in details
+ * about the transfer.
  *
- * \li A transferee channel leaving a bridge to run an app
- * \li A bridge of transferees running an app (via a local channel)
+ * \param transfer_msg The message to add details to.
+ * \param locals An array of local channel halves that each are in one of the involved bridges.
  *
- * \pre Bridges involved are locked. Channels involved are not locked.
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_attended_transfer_message_add_link(struct ast_attended_transfer_message *transfer_msg,
+               struct ast_channel *locals[2]);
+
+/*!
+ * \brief Publish an attended transfer
  *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the
- *        transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the
- *        transferer channel from that bridge
- * \param replace_channel The channel that will be replacing the transferee bridge
- *        transferer channel when a local channel is involved
- * \param dest_app The application that the channel or bridge is running upon transfer
- *        completion.
- * \param transferee_channel If a single channel is being transferred, this is it.
- *        If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it.
- *        If multiple parties are being transferred to, this is NULL.
- */
-void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_channel *replace_channel, const char *dest_app,
-               struct ast_channel *transferee_channel, struct ast_channel *target_channel);
+ * \param transfer_msg The transfer message to publish
+ */
+void ast_bridge_publish_attended_transfer(struct ast_attended_transfer_message *transfer_msg);
 
 /*!
  * \since 12
- * \brief Publish an attended transfer that results in two bridges linked by a local channel
- *
- * Publish an \ref ast_attended_transfer_message with the dest_type set to
- * \c AST_ATTENDED_TRANSFER_DEST_LINK. This occurs when two bridges are involved
- * in an attended transfer, but their properties do not allow for the bridges to
- * merge or to have channels moved off of the bridge. An example of this occurs when
- * attempting to transfer a ConfBridge to another bridge.
- *
- * When this type of transfer occurs, the two bridges continue to exist after the
- * transfer and a local channel is used to link the two bridges together.
- *
- * \pre Bridges involved are locked. Channels involved are not locked.
+ * \brief Message type for \ref ast_attended_transfer_message.
  *
- * \param is_external Indicates if the transfer was initiated externally
- * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param locals The local channels linking the bridges together.
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
- * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
- */
-void ast_bridge_publish_attended_transfer_link(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_channel *locals[2], struct ast_channel *transferee_channel,
-               struct ast_channel *target_channel);
+ * \retval Message type for \ref ast_attended_transfer_message.
+ */
+struct stasis_message_type *ast_attended_transfer_type(void);
 
 /*!
  * \brief Returns the most recent snapshot for the bridge.
index de55662..f46d314 100644 (file)
@@ -3835,26 +3835,6 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe
        return peer;
 }
 
-static void publish_blind_transfer_full(int is_external, enum ast_transfer_result result,
-               struct ast_channel *transferer, struct ast_bridge *bridge,
-               const char *context, const char *exten, struct ast_channel *transferee_channel,
-               struct ast_channel *replace_channel)
-{
-       struct ast_bridge_channel_pair pair;
-
-       pair.channel = transferer;
-       pair.bridge = bridge;
-
-       if (bridge) {
-               ast_bridge_lock(bridge);
-       }
-       ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten,
-               transferee_channel, replace_channel);
-       if (bridge) {
-               ast_bridge_unlock(bridge);
-       }
-}
-
 /*!
  * \internal
  * \brief Transfer an entire bridge to a specific destination.
@@ -3871,9 +3851,12 @@ static void publish_blind_transfer_full(int is_external, enum ast_transfer_resul
  * \param transferer The channel performing a transfer
  * \param bridge The bridge where the transfer is being performed
  * \param exten The destination extension for the blind transfer
- * \param transferee The party being transferred if there is only one
  * \param context The destination context for the blind transfer
- * \param hook Framehook to attach to local channel
+ * \param transferee The party being transferred if there is only one
+ * \param new_channel_cb Callback to call on channel that is created to
+ *        facilitate the blind transfer.
+ * \param user_data_wrapper User-provided data needed in new_channel_cb
+ * \param transfer_message The Stasis publication for this transfer.
  *
  * \return The success or failure of the operation
  */
@@ -3881,7 +3864,8 @@ static enum ast_transfer_result blind_transfer_bridge(int is_external,
                struct ast_channel *transferer, struct ast_bridge *bridge,
                const char *exten, const char *context, struct ast_channel *transferee,
                transfer_channel_cb new_channel_cb,
-               struct transfer_channel_data *user_data_wrapper)
+               struct transfer_channel_data *user_data_wrapper,
+               struct ast_blind_transfer_message *transfer_message)
 {
        struct ast_channel *local;
        char chan_name[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
@@ -3896,6 +3880,13 @@ static enum ast_transfer_result blind_transfer_bridge(int is_external,
 
        ast_channel_lock_both(local, transferer);
        ast_channel_req_accountcodes(local, transferer, AST_CHANNEL_REQUESTOR_REPLACEMENT);
+
+       transfer_message->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(local));
+       if (!transfer_message->replace_channel) {
+               ast_hangup(local);
+               return AST_BRIDGE_TRANSFER_FAIL;
+       }
+
        pbx_builtin_setvar_helper(local, BLINDTRANSFER, ast_channel_name(transferer));
        ast_channel_unlock(local);
        ast_channel_unlock(transferer);
@@ -3908,35 +3899,18 @@ static enum ast_transfer_result blind_transfer_bridge(int is_external,
                ast_hangup(local);
                return AST_BRIDGE_TRANSFER_FAIL;
        }
+
        if (ast_bridge_impart(bridge, local, transferer, NULL,
                AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
                ast_hangup(local);
                return AST_BRIDGE_TRANSFER_FAIL;
        }
-       publish_blind_transfer_full(is_external, AST_BRIDGE_TRANSFER_SUCCESS, transferer, bridge,
-               context, exten, transferee, local);
+
        return AST_BRIDGE_TRANSFER_SUCCESS;
 }
 
 /*!
  * \internal
- * \brief Base data to publish for stasis attended transfer messages
- */
-struct stasis_attended_transfer_publish_data {
-       /* The bridge between the transferer and transferee, and the transferer channel in this bridge */
-       struct ast_bridge_channel_pair to_transferee;
-       /* The bridge between the transferer and transfer target, and the transferer channel in this bridge */
-       struct ast_bridge_channel_pair to_transfer_target;
-       /* The Local;1 that will replace the transferee bridge transferer channel */
-       struct ast_channel *replace_channel;
-       /* The transferee channel. NULL if there is no transferee channel or if multiple parties are transferred */
-       struct ast_channel *transferee_channel;
-       /* The transfer target channel. NULL if there is no transfer target channel or if multiple parties are transferred */
-       struct ast_channel *target_channel;
-};
-
-/*!
- * \internal
  * \brief Get the transferee channel
  *
  * This is only applicable to cases where a transfer is occurring on a
@@ -3966,118 +3940,6 @@ static struct ast_channel *get_transferee(struct ao2_container *channels, struct
        return transferee;
 }
 
-
-static void stasis_publish_data_cleanup(struct stasis_attended_transfer_publish_data *publication)
-{
-       ast_channel_unref(publication->to_transferee.channel);
-       ast_channel_unref(publication->to_transfer_target.channel);
-       ast_channel_cleanup(publication->transferee_channel);
-       ast_channel_cleanup(publication->target_channel);
-       ao2_cleanup(publication->to_transferee.bridge);
-       ao2_cleanup(publication->to_transfer_target.bridge);
-       ao2_cleanup(publication->replace_channel);
-}
-
-/*!
- * \internal
- * \brief Set up base data for an attended transfer stasis publication
- *
- * \param to_transferee The original transferer channel, which may be bridged to a transferee
- * \param to_transferee_bridge The bridge that to_transferee is in.
- * \param to_transfer_target The second transferer channel, which may be bridged to a transfer target
- * \param to_target_bridge The bridge that to_transfer_target_is in.
- * \param[out] publication A structure to hold the other parameters
- */
-static void stasis_publish_data_init(struct ast_channel *to_transferee,
-               struct ast_bridge *to_transferee_bridge, struct ast_channel *to_transfer_target,
-               struct ast_bridge *to_target_bridge,
-               struct stasis_attended_transfer_publish_data *publication)
-{
-       memset(publication, 0, sizeof(*publication));
-       publication->to_transferee.channel = ast_channel_ref(to_transferee);
-       if (to_transferee_bridge) {
-               ao2_ref(to_transferee_bridge, +1);
-               publication->to_transferee.bridge = to_transferee_bridge;
-       }
-
-       publication->to_transfer_target.channel = ast_channel_ref(to_transfer_target);
-       if (to_target_bridge) {
-               ao2_ref(to_target_bridge, +1);
-               publication->to_transfer_target.bridge = to_target_bridge;
-       }
-
-       if (to_transferee_bridge) {
-               publication->transferee_channel = ast_bridge_peer(to_transferee_bridge, to_transferee);
-       }
-       if (to_target_bridge) {
-               publication->target_channel = ast_bridge_peer(to_target_bridge, to_transfer_target);
-       }
-}
-
-/*
- * \internal
- * \brief Publish a stasis attended transfer resulting in a bridge merge
- *
- * \param publication Base data about the attended transfer
- * \param final_bridge The surviving bridge of the attended transfer
- */
-static void publish_attended_transfer_bridge_merge(struct stasis_attended_transfer_publish_data *publication,
-               struct ast_bridge *final_bridge)
-{
-       ast_bridge_publish_attended_transfer_bridge_merge(1, AST_BRIDGE_TRANSFER_SUCCESS,
-                       &publication->to_transferee, &publication->to_transfer_target, final_bridge,
-                       publication->transferee_channel, publication->target_channel);
-}
-
-/*
- * \internal
- * \brief Publish a stasis attended transfer to an application
- *
- * \param publication Base data about the attended transfer
- * \param app The app that is running at the conclusion of the transfer
- */
-static void publish_attended_transfer_app(struct stasis_attended_transfer_publish_data *publication,
-               const char *app)
-{
-       ast_bridge_publish_attended_transfer_app(1, AST_BRIDGE_TRANSFER_SUCCESS,
-                       &publication->to_transferee, &publication->to_transfer_target,
-                       publication->replace_channel, app,
-                       publication->transferee_channel, publication->target_channel);
-}
-
-/*
- * \internal
- * \brief Publish a stasis attended transfer showing a link between bridges
- *
- * \param publication Base data about the attended transfer
- * \param local_channel1 Local channel in the original bridge
- * \param local_channel2 Local channel in the second bridge
- */
-static void publish_attended_transfer_link(struct stasis_attended_transfer_publish_data *publication,
-               struct ast_channel *local_channel1, struct ast_channel *local_channel2)
-{
-       struct ast_channel *locals[2] = { local_channel1, local_channel2 };
-
-       ast_bridge_publish_attended_transfer_link(1, AST_BRIDGE_TRANSFER_SUCCESS,
-                       &publication->to_transferee, &publication->to_transfer_target, locals,
-                       publication->transferee_channel, publication->target_channel);
-}
-
-/*
- * \internal
- * \brief Publish a stasis attended transfer failure
- *
- * \param publication Base data about the attended transfer
- * \param result The transfer result
- */
-static void publish_attended_transfer_fail(struct stasis_attended_transfer_publish_data *publication,
-               enum ast_transfer_result result)
-{
-       ast_bridge_publish_attended_transfer_fail(1, result, &publication->to_transferee,
-                       &publication->to_transfer_target, publication->transferee_channel,
-                       publication->target_channel);
-}
-
 /*!
  * \brief Perform an attended transfer of a bridge
  *
@@ -4102,7 +3964,7 @@ static void publish_attended_transfer_fail(struct stasis_attended_transfer_publi
  */
 static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *chan1,
                struct ast_channel *chan2, struct ast_bridge *bridge1, struct ast_bridge *bridge2,
-               struct stasis_attended_transfer_publish_data *publication)
+               struct ast_attended_transfer_message *transfer_msg)
 {
        static const char *dest = "_attended@transfer/m";
        struct ast_channel *local_chan;
@@ -4150,6 +4012,7 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
 
        if (bridge2) {
                RAII_VAR(struct ast_channel *, local_chan2, NULL, ao2_cleanup);
+               struct ast_channel *locals[2];
 
                ast_channel_lock(local_chan);
                local_chan2 = ast_local_get_peer(local_chan);
@@ -4157,11 +4020,12 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
 
                ast_assert(local_chan2 != NULL);
 
-               publish_attended_transfer_link(publication,
-                               local_chan, local_chan2);
+               locals[0] = local_chan;
+               locals[1] = local_chan2;
+
+               ast_attended_transfer_message_add_link(transfer_msg, locals);
        } else {
-               publication->replace_channel = ao2_bump(local_chan);
-               publish_attended_transfer_app(publication, app);
+               ast_attended_transfer_message_add_app(transfer_msg, app, local_chan);
        }
 
        ao2_cleanup(local_chan);
@@ -4267,14 +4131,6 @@ static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
        return bridge;
 }
 
-static void publish_blind_transfer(int is_external, enum ast_transfer_result result,
-               struct ast_channel *transferer, struct ast_bridge *bridge,
-               const char *context, const char *exten, struct ast_channel *transferee_channel)
-{
-       publish_blind_transfer_full(is_external, result, transferer, bridge, context,
-               exten, transferee_channel, NULL);
-}
-
 enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
                struct ast_channel *transferer, const char *exten, const char *context,
                transfer_channel_cb new_channel_cb, void *user_data)
@@ -4284,17 +4140,43 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
        RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
        RAII_VAR(struct ast_channel *, transferee, NULL, ast_channel_cleanup);
        RAII_VAR(struct transfer_channel_data *, user_data_wrapper, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_blind_transfer_message *, transfer_message, NULL, ao2_cleanup);
        int do_bridge_transfer;
        int transfer_prohibited;
        enum ast_transfer_result transfer_result;
 
+       transfer_message = ast_blind_transfer_message_create(is_external, transferer, exten, context);
+       if (!transfer_message) {
+               /* Out of memory. Not even possible to publish a Stasis message about the
+                * failure
+                */
+               ast_log(LOG_ERROR, "Unable to allocate memory for blind transfer publication from %s\n",
+                               ast_channel_name(transferer));
+               return AST_BRIDGE_TRANSFER_FAIL;
+       }
+
        bridge = acquire_bridge(transferer);
        if (!bridge) {
                transfer_result = AST_BRIDGE_TRANSFER_INVALID;
                goto publish;
        }
 
+       ast_bridge_lock(bridge);
+       transfer_message->bridge = ast_bridge_snapshot_create(bridge);
+       ast_bridge_unlock(bridge);
+       if (!transfer_message->bridge) {
+               transfer_result = AST_BRIDGE_TRANSFER_FAIL;
+               goto publish;
+       }
+
        transferee = ast_bridge_peer(bridge, transferer);
+       if (transferee) {
+               transfer_message->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee));
+               if (!transfer_message->transferee) {
+                       transfer_result = AST_BRIDGE_TRANSFER_FAIL;
+                       goto publish;
+               }
+       }
 
        ast_channel_lock(transferer);
        bridge_channel = ast_channel_get_bridge_channel(transferer);
@@ -4350,12 +4232,8 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
        set_transfer_variables_all(transferer, channels, 0);
 
        if (do_bridge_transfer) {
-               /* if blind_transfer_bridge succeeds, it publishes its own message */
                transfer_result = blind_transfer_bridge(is_external, transferer, bridge,
-                       exten, context, transferee, new_channel_cb, user_data_wrapper);
-               if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS)  {
-                       return transfer_result;
-               }
+                       exten, context, transferee, new_channel_cb, user_data_wrapper, transfer_message);
                goto publish;
        }
 
@@ -4376,7 +4254,8 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
        transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
 
 publish:
-       publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten, transferee);
+       transfer_message->result = transfer_result;
+       ast_bridge_publish_blind_transfer(transfer_message);
        return transfer_result;
 }
 
@@ -4443,7 +4322,7 @@ static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel
                struct ast_channel *to_transfer_target,
                struct ast_bridge_channel *to_target_bridge_channel,
                struct ast_bridge *to_transferee_bridge, struct ast_bridge *to_target_bridge,
-               struct stasis_attended_transfer_publish_data *publication)
+               struct ast_attended_transfer_message *transfer_msg)
 {
        struct ast_bridge_channel *kick_me[] = {
                        to_transferee_bridge_channel,
@@ -4489,20 +4368,16 @@ static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel
                 */
                if (to_transferee_bridge->inhibit_merge || to_transferee_bridge->dissolved ||
                                to_target_bridge->inhibit_merge || to_target_bridge->dissolved) {
-                       res = AST_BRIDGE_TRANSFER_INVALID;
-                       goto end;
+                       return AST_BRIDGE_TRANSFER_INVALID;
                }
 
-               /* Don't goto end here. attended_transfer_bridge will publish its own
-                * stasis message if it succeeds
-                */
                return attended_transfer_bridge(to_transferee, to_transfer_target,
-                       to_transferee_bridge, to_target_bridge, publication);
+                       to_transferee_bridge, to_target_bridge, transfer_msg);
        }
 
 end:
        if (res == AST_BRIDGE_TRANSFER_SUCCESS) {
-               publish_attended_transfer_bridge_merge(publication, final_bridge);
+               ast_attended_transfer_message_add_merge(transfer_msg, final_bridge);
        }
 
        return res;
@@ -4517,6 +4392,7 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
        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);
+       RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
        struct ast_bridge *the_bridge = NULL;
        struct ast_channel *chan_bridged;
        struct ast_channel *chan_unbridged;
@@ -4524,13 +4400,17 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
        int do_bridge_transfer;
        enum ast_transfer_result res;
        const char *app = NULL;
-       struct stasis_attended_transfer_publish_data publication;
 
        to_transferee_bridge = acquire_bridge(to_transferee);
        to_target_bridge = acquire_bridge(to_transfer_target);
 
-       stasis_publish_data_init(to_transferee, to_transferee_bridge,
-                       to_transfer_target, to_target_bridge, &publication);
+       transfer_msg = ast_attended_transfer_message_create(1, to_transferee, to_transferee_bridge,
+                       to_transfer_target, to_target_bridge, NULL, NULL);
+       if (!transfer_msg) {
+               ast_log(LOG_ERROR, "Unable to create Stasis publication for attended transfer from %s\n",
+                               ast_channel_name(to_transferee));
+               return AST_BRIDGE_TRANSFER_FAIL;
+       }
 
        /* They can't both be unbridged, you silly goose! */
        if (!to_transferee_bridge && !to_target_bridge) {
@@ -4595,7 +4475,7 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
                ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
                res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel,
                                to_transfer_target, to_target_bridge_channel,
-                               to_transferee_bridge, to_target_bridge, &publication);
+                               to_transferee_bridge, to_target_bridge, transfer_msg);
                ast_bridge_unlock(to_transferee_bridge);
                ast_bridge_unlock(to_target_bridge);
 
@@ -4636,7 +4516,7 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 
        if (do_bridge_transfer) {
                ast_bridge_lock(the_bridge);
-               res = attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL, &publication);
+               res = attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL, transfer_msg);
                ast_bridge_unlock(the_bridge);
                goto end;
        }
@@ -4655,32 +4535,12 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
 
        ast_bridge_remove(the_bridge, chan_bridged);
 
-       ast_bridge_lock(the_bridge);
-       publish_attended_transfer_app(&publication, app);
-       ast_bridge_unlock(the_bridge);
+       ast_attended_transfer_message_add_app(transfer_msg, app, NULL);
        res = AST_BRIDGE_TRANSFER_SUCCESS;
 
 end:
-       /* All successful transfer paths have published an appropriate stasis message.
-        * All failure paths have deferred publishing a stasis message until this point
-        */
-       if (res != AST_BRIDGE_TRANSFER_SUCCESS) {
-               if (to_transferee_bridge && to_target_bridge) {
-                       ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
-               } else if (the_bridge) {
-                       ast_bridge_lock(the_bridge);
-               }
-
-               publish_attended_transfer_fail(&publication, res);
-
-               if (to_transferee_bridge && to_target_bridge) {
-                       ast_bridge_unlock(to_transferee_bridge);
-                       ast_bridge_unlock(to_target_bridge);
-               } else if (the_bridge) {
-                       ast_bridge_unlock(the_bridge);
-               }
-       }
-       stasis_publish_data_cleanup(&publication);
+       transfer_msg->result = res;
+       ast_bridge_publish_attended_transfer(transfer_msg);
        return res;
 }
 
index a69750d..97892e3 100644 (file)
@@ -1606,33 +1606,21 @@ static void get_transfer_parties(struct ast_channel *transferer, struct ast_brid
 static void publish_transfer_success(struct attended_transfer_properties *props,
                struct ast_channel *transferee_channel, struct ast_channel *target_channel)
 {
-       struct ast_bridge_channel_pair transferee = {
-               .channel = props->transferer,
-               .bridge = props->transferee_bridge,
-       };
-       struct ast_bridge_channel_pair transfer_target = {
-               .channel = props->transferer,
-               .bridge = props->target_bridge,
-       };
-
-       if (transferee.bridge && transfer_target.bridge) {
-               ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
-       } else if (transferee.bridge) {
-               ast_bridge_lock(transferee.bridge);
-       } else if (transfer_target.bridge) {
-               ast_bridge_lock(transfer_target.bridge);
-       }
+       struct ast_attended_transfer_message *transfer_msg;
 
-       ast_bridge_publish_attended_transfer_bridge_merge(0, AST_BRIDGE_TRANSFER_SUCCESS,
-                       &transferee, &transfer_target, props->transferee_bridge, transferee_channel,
-                       target_channel);
+       transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
+                       props->transferee_bridge, props->transferer, props->target_bridge,
+                       transferee_channel, target_channel);
 
-       if (transferee.bridge) {
-               ast_bridge_unlock(transferee.bridge);
-       }
-       if (transfer_target.bridge) {
-               ast_bridge_unlock(transfer_target.bridge);
+       if (!transfer_msg) {
+               ast_log(LOG_ERROR, "Unable to publish successful attended transfer from %s\n",
+                               ast_channel_name(props->transferer));
+               return;
        }
+
+       ast_attended_transfer_message_add_merge(transfer_msg, props->transferee_bridge);
+       ast_bridge_publish_attended_transfer(transfer_msg);
+       ao2_cleanup(transfer_msg);
 }
 
 /*!
@@ -1641,37 +1629,22 @@ static void publish_transfer_success(struct attended_transfer_properties *props,
 static void publish_transfer_threeway(struct attended_transfer_properties *props,
                struct ast_channel *transferee_channel, struct ast_channel *target_channel)
 {
-       struct ast_bridge_channel_pair transferee = {
-               .channel = props->transferer,
-               .bridge = props->transferee_bridge,
-       };
-       struct ast_bridge_channel_pair transfer_target = {
-               .channel = props->transferer,
-               .bridge = props->target_bridge,
-       };
-       struct ast_bridge_channel_pair threeway = {
-               .channel = props->transferer,
-               .bridge = props->transferee_bridge,
-       };
+       struct ast_attended_transfer_message *transfer_msg;
 
-       if (transferee.bridge && transfer_target.bridge) {
-               ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
-       } else if (transferee.bridge) {
-               ast_bridge_lock(transferee.bridge);
-       } else if (transfer_target.bridge) {
-               ast_bridge_lock(transfer_target.bridge);
-       }
+       transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
+                       props->transferee_bridge, props->transferer, props->target_bridge,
+                       transferee_channel, target_channel);
 
-       ast_bridge_publish_attended_transfer_threeway(0, AST_BRIDGE_TRANSFER_SUCCESS,
-                       &transferee, &transfer_target, &threeway, transferee_channel,
-                       target_channel);
-
-       if (transferee.bridge) {
-               ast_bridge_unlock(transferee.bridge);
-       }
-       if (transfer_target.bridge) {
-               ast_bridge_unlock(transfer_target.bridge);
+       if (!transfer_msg) {
+               ast_log(LOG_ERROR, "Unable to publish successful three-way transfer from %s\n",
+                               ast_channel_name(props->transferer));
+               return;
        }
+
+       ast_attended_transfer_message_add_threeway(transfer_msg, props->transferer,
+                       props->transferee_bridge);
+       ast_bridge_publish_attended_transfer(transfer_msg);
+       ao2_cleanup(transfer_msg);
 }
 
 /*!
@@ -1679,38 +1652,21 @@ static void publish_transfer_threeway(struct attended_transfer_properties *props
  */
 static void publish_transfer_fail(struct attended_transfer_properties *props)
 {
-       struct ast_bridge_channel_pair transferee = {
-               .channel = props->transferer,
-               .bridge = props->transferee_bridge,
-       };
-       struct ast_bridge_channel_pair transfer_target = {
-               .channel = props->transferer,
-               .bridge = props->target_bridge,
-       };
-       struct ast_channel *transferee_channel;
-       struct ast_channel *target_channel;
-
-       if (transferee.bridge && transfer_target.bridge) {
-               ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
-       } else if (transferee.bridge) {
-               ast_bridge_lock(transferee.bridge);
-       } else if (transfer_target.bridge) {
-               ast_bridge_lock(transfer_target.bridge);
-       }
+       struct ast_attended_transfer_message *transfer_msg;
 
-       get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge,
-                       &transferee_channel, &target_channel);
-       ast_bridge_publish_attended_transfer_fail(0, AST_BRIDGE_TRANSFER_FAIL,
-                       &transferee, &transfer_target, transferee_channel, target_channel);
-       ast_channel_cleanup(transferee_channel);
-       ast_channel_cleanup(target_channel);
+       transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
+                       props->transferee_bridge, props->transferer, props->target_bridge,
+                       NULL, NULL);
 
-       if (transferee.bridge) {
-               ast_bridge_unlock(transferee.bridge);
-       }
-       if (transfer_target.bridge) {
-               ast_bridge_unlock(transfer_target.bridge);
+       if (!transfer_msg) {
+               ast_log(LOG_ERROR, "Unable to publish failed transfer from %s\n",
+                               ast_channel_name(props->transferer));
+               return;
        }
+
+       transfer_msg->result = AST_BRIDGE_TRANSFER_FAIL;
+       ast_bridge_publish_attended_transfer(transfer_msg);
+       ao2_cleanup(transfer_msg);
 }
 
 /*!
index 69bb264..9463603 100644 (file)
@@ -1359,8 +1359,8 @@ static void cel_blind_transfer_cb(
        struct stasis_message *message)
 {
        struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
-       struct ast_channel_snapshot *chan_snapshot = transfer_msg->to_transferee.channel_snapshot;
-       struct ast_bridge_snapshot *bridge_snapshot = transfer_msg->to_transferee.bridge_snapshot;
+       struct ast_channel_snapshot *chan_snapshot = transfer_msg->transferer;
+       struct ast_bridge_snapshot *bridge_snapshot = transfer_msg->bridge;
        struct ast_json *extra;
 
        if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
index 16fda2d..fbdb98c 100644 (file)
@@ -596,16 +596,19 @@ struct ast_json *ast_bridge_snapshot_to_json(
  * \retval 0 Success
  * \retval non-zero Failure
  */
-static int bridge_channel_snapshot_pair_init(struct ast_bridge_channel_pair *pair, struct ast_bridge_channel_snapshot_pair *snapshot_pair)
+static int bridge_channel_snapshot_pair_init(struct ast_channel *channel, struct ast_bridge *bridge,
+               struct ast_bridge_channel_snapshot_pair *snapshot_pair)
 {
-       if (pair->bridge) {
-               snapshot_pair->bridge_snapshot = ast_bridge_snapshot_create(pair->bridge);
+       if (bridge) {
+               ast_bridge_lock(bridge);
+               snapshot_pair->bridge_snapshot = ast_bridge_snapshot_create(bridge);
+               ast_bridge_unlock(bridge);
                if (!snapshot_pair->bridge_snapshot) {
                        return -1;
                }
        }
 
-       snapshot_pair->channel_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(pair->channel));
+       snapshot_pair->channel_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(channel));
        if (!snapshot_pair->channel_snapshot) {
                return -1;
        }
@@ -642,7 +645,7 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg,
        struct ast_json *json_replace = NULL;
        const struct timeval *tv = stasis_message_timestamp(msg);
 
-       json_transferer = ast_channel_snapshot_to_json(transfer_msg->to_transferee.channel_snapshot, sanitize);
+       json_transferer = ast_channel_snapshot_to_json(transfer_msg->transferer, sanitize);
        if (!json_transferer) {
                return NULL;
        }
@@ -690,9 +693,9 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg,
                return NULL;
        }
 
-       if (transfer_msg->to_transferee.bridge_snapshot) {
+       if (transfer_msg->bridge) {
                struct ast_json *json_bridge = ast_bridge_snapshot_to_json(
-                               transfer_msg->to_transferee.bridge_snapshot, sanitize);
+                               transfer_msg->bridge, sanitize);
 
                if (!json_bridge || ast_json_object_set(out, "bridge", json_bridge)) {
                        ast_json_unref(out);
@@ -715,13 +718,13 @@ static struct ast_manager_event_blob *blind_transfer_to_ami(struct stasis_messag
        }
 
        transferer_state = ast_manager_build_channel_state_string_prefix(
-                       transfer_msg->to_transferee.channel_snapshot, "Transferer");
+                       transfer_msg->transferer, "Transferer");
        if (!transferer_state) {
                return NULL;
        }
 
-       if (transfer_msg->to_transferee.bridge_snapshot) {
-               bridge_state = ast_manager_build_bridge_state_string(transfer_msg->to_transferee.bridge_snapshot);
+       if (transfer_msg->bridge) {
+               bridge_state = ast_manager_build_bridge_state_string(transfer_msg->bridge);
                if (!bridge_state) {
                        return NULL;
                }
@@ -756,48 +759,48 @@ static void blind_transfer_dtor(void *obj)
 {
        struct ast_blind_transfer_message *msg = obj;
 
-       bridge_channel_snapshot_pair_cleanup(&msg->to_transferee);
+       ao2_cleanup(msg->transferer);
+       ao2_cleanup(msg->bridge);
        ao2_cleanup(msg->transferee);
+       ao2_cleanup(msg->replace_channel);
 }
 
-void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferer, const char *context, const char *exten,
-               struct ast_channel *transferee_channel, struct ast_channel *replace_channel)
+struct ast_blind_transfer_message *ast_blind_transfer_message_create(int is_external,
+               struct ast_channel *transferer, const char *exten, const char *context)
 {
        struct ast_blind_transfer_message *msg;
-       struct stasis_message *stasis;
 
        msg = ao2_alloc(sizeof(*msg), blind_transfer_dtor);
        if (!msg) {
-               return;
+               return NULL;
        }
 
-       if (bridge_channel_snapshot_pair_init(transferer, &msg->to_transferee)) {
+       msg->transferer = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferer));
+       if (!msg->transferer) {
                ao2_cleanup(msg);
-               return;
+               return NULL;
        }
 
-       if (transferee_channel) {
-               msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee_channel));
-       }
-       if (replace_channel) {
-               msg->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(replace_channel));
-       }
        msg->is_external = is_external;
-       msg->result = result;
        ast_copy_string(msg->context, context, sizeof(msg->context));
        ast_copy_string(msg->exten, exten, sizeof(msg->exten));
 
-       stasis = stasis_message_create(ast_blind_transfer_type(), msg);
+       return msg;
+}
+
+
+void ast_bridge_publish_blind_transfer(struct ast_blind_transfer_message *transfer_message)
+{
+       struct stasis_message *stasis;
+
+       stasis = stasis_message_create(ast_blind_transfer_type(), transfer_message);
        if (!stasis) {
-               ao2_cleanup(msg);
                return;
        }
 
        stasis_publish(ast_bridge_topic_all(), stasis);
 
        ao2_cleanup(stasis);
-       ao2_cleanup(msg);
 }
 
 static struct ast_json *attended_transfer_to_json(struct stasis_message *msg,
@@ -1051,195 +1054,130 @@ static void attended_transfer_dtor(void *obj)
        }
 }
 
-static struct ast_attended_transfer_message *attended_transfer_message_create(int is_external,
-               enum ast_transfer_result result, struct ast_bridge_channel_pair *transferee,
-               struct ast_bridge_channel_pair *target, struct ast_channel *replace_channel,
-               struct ast_channel *transferee_channel, struct ast_channel *target_channel)
+struct ast_attended_transfer_message *ast_attended_transfer_message_create(int is_external,
+               struct ast_channel *to_transferee, struct ast_bridge *transferee_bridge,
+               struct ast_channel *to_transfer_target, struct ast_bridge *target_bridge,
+               struct ast_channel *transferee, struct ast_channel *transfer_target)
 {
-       RAII_VAR(struct ast_attended_transfer_message *, msg, NULL, ao2_cleanup);
+       struct ast_attended_transfer_message *transfer_msg;
 
-       msg = ao2_alloc(sizeof(*msg), attended_transfer_dtor);
-       if (!msg) {
+       transfer_msg = ao2_alloc(sizeof(*transfer_msg), attended_transfer_dtor);
+       if (!transfer_msg) {
                return NULL;
        }
 
-       if (bridge_channel_snapshot_pair_init(transferee, &msg->to_transferee) ||
-                       bridge_channel_snapshot_pair_init(target, &msg->to_transfer_target)) {
+       if (bridge_channel_snapshot_pair_init(to_transferee, transferee_bridge, &transfer_msg->to_transferee) ||
+                       bridge_channel_snapshot_pair_init(to_transfer_target, target_bridge, &transfer_msg->to_transfer_target)) {
+               ao2_cleanup(transfer_msg);
                return NULL;
        }
 
-       if (replace_channel) {
-               msg->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(replace_channel));
-               if (!msg->replace_channel) {
+       if (transferee) {
+               transfer_msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee));
+               if (!transfer_msg->transferee) {
+                       ao2_cleanup(transfer_msg);
                        return NULL;
                }
+       } else if (transferee_bridge) {
+               transferee = ast_bridge_peer(transferee_bridge, to_transferee);
+               if (transferee) {
+                       transfer_msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee));
+                       ao2_cleanup(transferee);
+                       if (!transfer_msg->transferee) {
+                               ao2_cleanup(transfer_msg);
+                               return NULL;
+                       }
+               }
        }
 
-       if (transferee_channel) {
-               msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee_channel));
-       }
-       if (target_channel) {
-               msg->target = ast_channel_snapshot_get_latest(ast_channel_uniqueid(target_channel));
-       }
-       msg->is_external = is_external;
-       msg->result = result;
-
-       ao2_ref(msg, +1);
-       return msg;
-}
-
-void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_channel *transferee_channel, struct ast_channel *target_channel)
-{
-       RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
-       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-
-       if (!ast_attended_transfer_type()) {
-               return;
-       }
-
-       transfer_msg = attended_transfer_message_create(is_external, result,
-                       transferee, target, NULL, transferee_channel, target_channel);
-       if (!transfer_msg) {
-               return;
-       }
-
-       transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_FAIL;
-
-       msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
-       if (!msg) {
-               return;
+       if (transfer_target) {
+               transfer_msg->target = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transfer_target));
+               if (!transfer_msg->target) {
+                       ao2_cleanup(transfer_msg);
+                       return NULL;
+               }
+       } else if (target_bridge) {
+               transfer_target = ast_bridge_peer(target_bridge, to_transfer_target);
+               if (transfer_target) {
+                       transfer_msg->target = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transfer_target));
+                       ao2_cleanup(transfer_target);
+                       if (!transfer_msg->target) {
+                               ao2_cleanup(transfer_msg);
+                               return NULL;
+                       }
+               }
        }
 
-       stasis_publish(ast_bridge_topic_all(), msg);
+       return transfer_msg;
 }
 
-void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_bridge *final_bridge, struct ast_channel *transferee_channel,
-               struct ast_channel *target_channel)
+int ast_attended_transfer_message_add_merge(struct ast_attended_transfer_message *transfer_msg,
+               struct ast_bridge *final_bridge)
 {
-       RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
-       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-
-       if (!ast_attended_transfer_type()) {
-               return;
-       }
-
-       transfer_msg = attended_transfer_message_create(is_external, result,
-                       transferee, target, NULL, transferee_channel, target_channel);
-       if (!transfer_msg) {
-               return;
-       }
-
        transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE;
        ast_copy_string(transfer_msg->dest.bridge, final_bridge->uniqueid,
                        sizeof(transfer_msg->dest.bridge));
 
-       msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
-       if (!msg) {
-               return;
-       }
-
-       stasis_publish(ast_bridge_topic_all(), msg);
+       return 0;
 }
 
-void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_bridge_channel_pair *final_pair, struct ast_channel *transferee_channel,
-               struct ast_channel *target_channel)
+int ast_attended_transfer_message_add_threeway(struct ast_attended_transfer_message *transfer_msg,
+               struct ast_channel *survivor_channel, struct ast_bridge *survivor_bridge)
 {
-       RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
-       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-
-       if (!ast_attended_transfer_type()) {
-               return;
-       }
-
-       transfer_msg = attended_transfer_message_create(is_external, result,
-                       transferee, target, NULL, transferee_channel, target_channel);
-       if (!transfer_msg) {
-               return;
-       }
-
        transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_THREEWAY;
-       if (final_pair->channel == transferee->channel) {
+
+       if (!strcmp(ast_channel_uniqueid(survivor_channel), transfer_msg->to_transferee.channel_snapshot->uniqueid)) {
                transfer_msg->dest.threeway.channel_snapshot = transfer_msg->to_transferee.channel_snapshot;
        } else {
                transfer_msg->dest.threeway.channel_snapshot = transfer_msg->to_transfer_target.channel_snapshot;
        }
 
-       if (final_pair->bridge == transferee->bridge) {
+       if (!strcmp(survivor_bridge->uniqueid, transfer_msg->to_transferee.bridge_snapshot->uniqueid)) {
                transfer_msg->dest.threeway.bridge_snapshot = transfer_msg->to_transferee.bridge_snapshot;
        } else {
                transfer_msg->dest.threeway.bridge_snapshot = transfer_msg->to_transfer_target.bridge_snapshot;
        }
 
-       msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
-       if (!msg) {
-               return;
-       }
-
-       stasis_publish(ast_bridge_topic_all(), msg);
+       return 0;
 }
 
-void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_channel *replace_channel, const char *dest_app,
-               struct ast_channel *transferee_channel, struct ast_channel *target_channel)
+int ast_attended_transfer_message_add_app(struct ast_attended_transfer_message *transfer_msg,
+               const char *app, struct ast_channel *replace_channel)
 {
-       RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
-       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-
-       if (!ast_attended_transfer_type()) {
-               return;
-       }
-
-       transfer_msg = attended_transfer_message_create(is_external, result,
-                       transferee, target, replace_channel, transferee_channel, target_channel);
-       if (!transfer_msg) {
-               return;
-       }
-
        transfer_msg->dest_type = replace_channel ? AST_ATTENDED_TRANSFER_DEST_LOCAL_APP : AST_ATTENDED_TRANSFER_DEST_APP;
-       ast_copy_string(transfer_msg->dest.app, dest_app, sizeof(transfer_msg->dest.app));
 
-       msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
-       if (!msg) {
-               return;
+       if (replace_channel) {
+               transfer_msg->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(replace_channel));
+               if (!transfer_msg->replace_channel) {
+                       return -1;
+               }
        }
 
-       stasis_publish(ast_bridge_topic_all(), msg);
+       ast_copy_string(transfer_msg->dest.app, app, sizeof(transfer_msg->dest.app));
+
+       return 0;
 }
 
-void ast_bridge_publish_attended_transfer_link(int is_external, enum ast_transfer_result result,
-               struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-               struct ast_channel *locals[2], struct ast_channel *transferee_channel,
-               struct ast_channel *target_channel)
+int ast_attended_transfer_message_add_link(struct ast_attended_transfer_message *transfer_msg,
+               struct ast_channel *locals[2])
 {
-       RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
-       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
        int i;
 
-       if (!ast_attended_transfer_type()) {
-               return;
-       }
-
-       transfer_msg = attended_transfer_message_create(is_external, result,
-                       transferee, target, NULL, transferee_channel, target_channel);
-       if (!transfer_msg) {
-               return;
-       }
-
        transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_LINK;
        for (i = 0; i < 2; ++i) {
                transfer_msg->dest.links[i] = ast_channel_snapshot_get_latest(ast_channel_uniqueid(locals[i]));
                if (!transfer_msg->dest.links[i]) {
-                       return;
+                       return -1;
                }
        }
 
+       return 0;
+}
+
+void ast_bridge_publish_attended_transfer(struct ast_attended_transfer_message *transfer_msg)
+{
+       RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+
        msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
        if (!msg) {
                return;
index cda1c04..07e273f 100644 (file)
@@ -722,14 +722,13 @@ static void bridge_blind_transfer_handler(void *data, struct stasis_subscription
 {
        struct stasis_app *app = data;
        struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
-       struct ast_bridge_snapshot *bridge = transfer_msg->to_transferee.bridge_snapshot;
+       struct ast_bridge_snapshot *bridge = transfer_msg->bridge;
 
        if (transfer_msg->replace_channel) {
-               set_replacement_channel(transfer_msg->to_transferee.channel_snapshot,
-                               transfer_msg->replace_channel);
+               set_replacement_channel(transfer_msg->transferer, transfer_msg->replace_channel);
        }
 
-       if (bridge_app_subscribed(app, transfer_msg->to_transferee.channel_snapshot->uniqueid) ||
+       if (bridge_app_subscribed(app, transfer_msg->transferer->uniqueid) ||
                (bridge && bridge_app_subscribed_involved(app, bridge))) {
                stasis_publish(app->topic, message);
        }
index 3164d41..c86f429 100644 (file)
@@ -1243,9 +1243,9 @@ AST_TEST_DEFINE(test_cel_blind_transfer)
        RAII_VAR(struct ast_channel *, chan_alice, NULL, safe_channel_release);
        RAII_VAR(struct ast_channel *, chan_bob, NULL, safe_channel_release);
        RAII_VAR(struct ast_bridge *, bridge, NULL, safe_bridge_destroy);
+       RAII_VAR(struct ast_blind_transfer_message *, transfer_msg, NULL, ao2_cleanup);
        struct ast_party_caller alice_caller = ALICE_CALLERID;
        struct ast_party_caller bob_caller = BOB_CALLERID;
-       struct ast_bridge_channel_pair pair;
 
        switch (cmd) {
        case TEST_INIT:
@@ -1271,12 +1271,21 @@ AST_TEST_DEFINE(test_cel_blind_transfer)
        BRIDGE_ENTER(chan_bob, bridge);
        BRIDGE_ENTER(chan_alice, bridge);
 
-       pair.bridge = bridge;
-       pair.channel = chan_alice;
        ast_bridge_lock(bridge);
-       ast_bridge_publish_blind_transfer(1, AST_BRIDGE_TRANSFER_SUCCESS,
-               &pair, "transfer_context", "transfer_extension", NULL, NULL);
+       transfer_msg = ast_blind_transfer_message_create(1, chan_alice,
+                       "transfer_extension", "transfer_context");
+       if (!transfer_msg) {
+               ast_test_status_update(test, "Failed to create transfer Stasis message\n");
+               return AST_TEST_FAIL;
+       }
+       transfer_msg->bridge = ast_bridge_snapshot_create(bridge);
+       if (!transfer_msg->bridge) {
+               ast_test_status_update(test, "Failed to create bridge snapshot\n");
+               return AST_TEST_FAIL;
+       }
        ast_bridge_unlock(bridge);
+       transfer_msg->result = AST_BRIDGE_TRANSFER_SUCCESS;
+       ast_bridge_publish_blind_transfer(transfer_msg);
        BLINDTRANSFER_EVENT(chan_alice, bridge, "transfer_extension", "transfer_context");
 
        BRIDGE_EXIT(chan_alice, bridge);