diff --git a/apps/app_queue.c b/apps/app_queue.c index 8d1653cac3..4820c662d6 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -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; } diff --git a/include/asterisk/stasis_bridges.h b/include/asterisk/stasis_bridges.h index 699e276264..d549e46202 100644 --- a/include/asterisk/stasis_bridges.h +++ b/include/asterisk/stasis_bridges.h @@ -268,14 +268,6 @@ struct ast_bridge_channel_snapshot_pair { struct ast_channel_snapshot *channel_snapshot; }; -/*! - * \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 */ @@ -304,6 +298,21 @@ struct ast_blind_transfer_message { struct ast_channel_snapshot *replace_channel; }; +/*! + * \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 * @@ -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. diff --git a/main/bridge.c b/main/bridge.c index de55662447..f46d314230 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -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,33 +3899,16 @@ 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 @@ -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; } diff --git a/main/bridge_basic.c b/main/bridge_basic.c index a69750d152..97892e395d 100644 --- a/main/bridge_basic.c +++ b/main/bridge_basic.c @@ -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); } /*! diff --git a/main/cel.c b/main/cel.c index 69bb2649e1..9463603e6e 100644 --- a/main/cel.c +++ b/main/cel.c @@ -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) { diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c index 16fda2d491..fbdb98c830 100644 --- a/main/stasis_bridges.c +++ b/main/stasis_bridges.c @@ -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; diff --git a/res/stasis/app.c b/res/stasis/app.c index cda1c045d5..07e273fa74 100644 --- a/res/stasis/app.c +++ b/res/stasis/app.c @@ -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); } diff --git a/tests/test_cel.c b/tests/test_cel.c index 3164d4194b..c86f429ac1 100644 --- a/tests/test_cel.c +++ b/tests/test_cel.c @@ -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);