diff options
-rw-r--r-- | channels/chan_dahdi.c | 2 | ||||
-rw-r--r-- | channels/chan_mgcp.c | 2 | ||||
-rw-r--r-- | channels/chan_sip.c | 4 | ||||
-rw-r--r-- | channels/sig_analog.c | 2 | ||||
-rw-r--r-- | include/asterisk/bridge.h | 18 | ||||
-rw-r--r-- | include/asterisk/parking.h | 12 | ||||
-rw-r--r-- | main/bridge.c | 28 | ||||
-rw-r--r-- | main/bridge_basic.c | 4 | ||||
-rw-r--r-- | main/parking.c | 8 | ||||
-rw-r--r-- | res/parking/parking_applications.c | 2 | ||||
-rw-r--r-- | res/parking/parking_bridge_features.c | 88 | ||||
-rw-r--r-- | res/res_pjsip_refer.c | 28 |
12 files changed, 158 insertions, 40 deletions
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index b4c8ec366..71e5831e0 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -9741,7 +9741,7 @@ static void *analog_ss_thread(void *data) bridge_channel = ast_channel_get_bridge_channel(p->subs[SUB_THREEWAY].owner); ast_channel_unlock(p->subs[SUB_THREEWAY].owner); if (bridge_channel) { - if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) { + if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten, NULL, NULL)) { /* * Swap things around between the three-way and real call so we * can hear where the channel got parked. diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index 1955edaea..2d0afe29d 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -3167,7 +3167,7 @@ static void *mgcp_ss(void *data) ast_channel_lock(chan); bridge_channel = ast_channel_get_bridge_channel(chan); ast_channel_unlock(chan); - if (bridge_channel && !ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), p->dtmf_buf)) { + if (bridge_channel && !ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), p->dtmf_buf, NULL, NULL)) { ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan)); } break; diff --git a/channels/chan_sip.c b/channels/chan_sip.c index a9a13790c..6c49236ce 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -26081,10 +26081,10 @@ struct blind_transfer_cb_data { * \param user_data A blind_transfer_cb_data struct * \param transfer_type Unused */ -static void blind_transfer_cb(struct ast_channel *chan, void *user_data, +static void blind_transfer_cb(struct ast_channel *chan, struct transfer_channel_data *user_data_wrapper, enum ast_transfer_type transfer_type) { - struct blind_transfer_cb_data *cb_data = user_data; + struct blind_transfer_cb_data *cb_data = user_data_wrapper->data; pbx_builtin_setvar_helper(chan, "SIPTRANSFER", "yes"); pbx_builtin_setvar_helper(chan, "SIPTRANSFER_REFERER", cb_data->referred_by); diff --git a/channels/sig_analog.c b/channels/sig_analog.c index 21b832164..9957074d1 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -2259,7 +2259,7 @@ static void *__analog_ss_thread(void *data) bridge_channel = ast_channel_get_bridge_channel(p->subs[ANALOG_SUB_THREEWAY].owner); ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner); if (bridge_channel) { - if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) { + if (!ast_parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten, NULL, NULL)) { /* * Swap things around between the three-way and real call so we * can hear where the channel got parked. diff --git a/include/asterisk/bridge.h b/include/asterisk/bridge.h index 8768fe1be..d23d4b5bc 100644 --- a/include/asterisk/bridge.h +++ b/include/asterisk/bridge.h @@ -904,6 +904,22 @@ enum ast_transfer_type { }; /*! + * \brief AO2 object that wraps data for transfer_channel_cb + */ +struct transfer_channel_data { + void *data; /*! Data to be used by the transfer_channel_cb -- note that this + * pointer is going to be pointing to something on the stack, so + * it must not be used at any point after returning from the + * transfer_channel_cb. */ + int completed; /*! Initially 0, This will be set to 1 by either the transfer + * code or by transfer code hooks (e.g. parking) when the + * transfer is completed and any remaining actions have taken + * place (e.g. parking announcements). It will never be reset + * to 0. This is used for deferring progress for channel + * drivers that support deferred progress. */ +}; + +/*! * \brief Callback function type called during blind transfers * * A caller of ast_bridge_transfer_blind() may wish to set data on @@ -914,7 +930,7 @@ enum ast_transfer_type { * \param user_data User-provided data needed in the callback * \param transfer_type The type of transfer being completed */ -typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data, +typedef void (*transfer_channel_cb)(struct ast_channel *chan, struct transfer_channel_data *user_data, enum ast_transfer_type transfer_type); /*! diff --git a/include/asterisk/parking.h b/include/asterisk/parking.h index 8b2b4b409..a8832cdb8 100644 --- a/include/asterisk/parking.h +++ b/include/asterisk/parking.h @@ -24,6 +24,7 @@ */ #include "asterisk/stringfields.h" +#include "asterisk/bridge.h" /*! * \brief The default parking application that Asterisk expects. @@ -163,6 +164,8 @@ struct ast_parking_bridge_feature_fn_table { * \param parker The \ref bridge_channel object that is initiating the parking * \param context The context to blind transfer to * \param exten The extension to blind transfer to + * \param parked_channel_cb Execute the following function on the the channel that gets parked + * \param parked_channel_data Data for the parked_channel_cb * * \note If the bridge \ref parker is in has more than one other occupant, the entire * bridge will be parked using a Local channel @@ -172,7 +175,8 @@ struct ast_parking_bridge_feature_fn_table { * \retval 0 on success * \retval non-zero on error */ - int (* parking_blind_transfer_park)(struct ast_bridge_channel *parker, const char *context, const char *exten); + int (* parking_blind_transfer_park)(struct ast_bridge_channel *parker, const char *context, + const char *exten, transfer_channel_cb parked_channel_cb, struct transfer_channel_data *parked_channel_data); /*! * \brief Perform a direct park on a channel in a bridge. @@ -224,6 +228,9 @@ int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t * \param parker The \ref bridge_channel object that is initiating the parking * \param context The context to blind transfer to * \param exten The extension to blind transfer to + * \param exten The extension to blind transfer to + * \param parked_channel_cb Execute the following function on the the channel that gets parked + * \param parked_channel_data Data for the parked_channel_cb * * \note If the bridge \ref parker is in has more than one other occupant, the entire * bridge will be parked using a Local channel @@ -233,7 +240,8 @@ int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t * \retval 0 on success * \retval non-zero on error */ -int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const char *context, const char *exten); +int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const char *context, + const char *exten, transfer_channel_cb parked_channel_cb, struct transfer_channel_data *parked_channel_data); /*! * \brief Perform a direct park on a channel in a bridge. diff --git a/main/bridge.c b/main/bridge.c index 6fdf0d554..fa4e3c699 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -3718,7 +3718,7 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe */ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transferer, struct ast_bridge *bridge, const char *exten, const char *context, - transfer_channel_cb new_channel_cb, void *user_data) + transfer_channel_cb new_channel_cb, struct transfer_channel_data *user_data_wrapper) { struct ast_channel *local; char chan_name[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2]; @@ -3734,7 +3734,7 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf pbx_builtin_setvar_helper(local, BLINDTRANSFER, ast_channel_name(transferer)); if (new_channel_cb) { - new_channel_cb(local, user_data, AST_BRIDGE_TRANSFER_MULTI_PARTY); + new_channel_cb(local, user_data_wrapper, AST_BRIDGE_TRANSFER_MULTI_PARTY); } if (ast_call(local, chan_name, 0)) { @@ -3968,7 +3968,9 @@ static struct ast_channel *get_transferee(struct ao2_container *channels, struct return transferee; } -static enum ast_transfer_result try_parking(struct ast_channel *transferer, const char *context, const char *exten) +static enum ast_transfer_result try_parking(struct ast_channel *transferer, + const char *context, const char *exten, transfer_channel_cb new_channel_cb, + struct transfer_channel_data *user_data_wrapper) { RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup); @@ -3985,7 +3987,7 @@ static enum ast_transfer_result try_parking(struct ast_channel *transferer, cons } if (ast_parking_blind_transfer_park(transferer_bridge_channel, - context, exten)) { + context, exten, new_channel_cb, user_data_wrapper)) { return AST_BRIDGE_TRANSFER_FAIL; } @@ -4089,6 +4091,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup); RAII_VAR(struct ast_channel *, transferee, NULL, ast_channel_cleanup); + RAII_VAR(struct transfer_channel_data *, user_data_wrapper, NULL, ao2_cleanup); int do_bridge_transfer; int transfer_prohibited; enum ast_transfer_result transfer_result; @@ -4106,14 +4109,25 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, goto publish; } + user_data_wrapper = ao2_alloc(sizeof(*user_data_wrapper), NULL); + if (!user_data_wrapper) { + transfer_result = AST_BRIDGE_TRANSFER_FAIL; + goto publish; + } + + user_data_wrapper->data = user_data; + /* Take off hold if they are on hold. */ ast_bridge_channel_write_unhold(bridge_channel); - transfer_result = try_parking(transferer, context, exten); + transfer_result = try_parking(transferer, context, exten, new_channel_cb, user_data_wrapper); if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS) { goto publish; } + /* Since parking didn't take control of the user_data_wrapper, we are just going to raise the completed flag now. */ + user_data_wrapper->completed = 1; + { SCOPED_LOCK(lock, bridge, ast_bridge_lock, ast_bridge_unlock); @@ -4142,7 +4156,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, if (do_bridge_transfer) { transfer_result = blind_transfer_bridge(transferer, bridge, exten, context, - new_channel_cb, user_data); + new_channel_cb, user_data_wrapper); goto publish; } @@ -4155,7 +4169,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, } if (bridge_channel_internal_queue_blind_transfer(transferee, exten, context, - new_channel_cb, user_data)) { + new_channel_cb, user_data_wrapper)) { transfer_result = AST_BRIDGE_TRANSFER_FAIL; goto publish; } diff --git a/main/bridge_basic.c b/main/bridge_basic.c index a5827f2ad..cee56b1b5 100644 --- a/main/bridge_basic.c +++ b/main/bridge_basic.c @@ -3145,10 +3145,10 @@ static int feature_attended_transfer(struct ast_bridge_channel *bridge_channel, return 0; } -static void blind_transfer_cb(struct ast_channel *new_channel, void *user_data, +static void blind_transfer_cb(struct ast_channel *new_channel, struct transfer_channel_data *user_data_wrapper, enum ast_transfer_type transfer_type) { - struct ast_channel *transferer_channel = user_data; + struct ast_channel *transferer_channel = user_data_wrapper->data; if (transfer_type == AST_BRIDGE_TRANSFER_MULTI_PARTY) { copy_caller_data(new_channel, transferer_channel); diff --git a/main/parking.c b/main/parking.c index db274a3ff..f7f1dfb5c 100644 --- a/main/parking.c +++ b/main/parking.c @@ -142,7 +142,9 @@ int ast_parking_park_bridge_channel(struct ast_bridge_channel *parkee, const cha return table->parking_park_bridge_channel(parkee, parkee_uuid, parker_uuid, app_data); } -int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const char *context, const char *exten) +int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, + const char *context, const char *exten, transfer_channel_cb parked_channel_cb, + struct transfer_channel_data *parked_channel_data) { RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table, ao2_global_obj_ref(parking_provider), ao2_cleanup); @@ -153,10 +155,10 @@ int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker, const cha if (table->module_info) { SCOPED_MODULE_USE(table->module_info->self); - return table->parking_blind_transfer_park(parker, context, exten); + return table->parking_blind_transfer_park(parker, context, exten, parked_channel_cb, parked_channel_data); } - return table->parking_blind_transfer_park(parker, context, exten); + return table->parking_blind_transfer_park(parker, context, exten, parked_channel_cb, parked_channel_data); } int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t length) diff --git a/res/parking/parking_applications.c b/res/parking/parking_applications.c index 3d2eecd29..5500e3316 100644 --- a/res/parking/parking_applications.c +++ b/res/parking/parking_applications.c @@ -516,6 +516,7 @@ static int park_app_exec(struct ast_channel *chan, const char *data) if (!silence_announcements && !transferer) { ast_stream_and_wait(chan, "pbx-parkingfailed", ""); } + publish_parked_call_failure(chan); return 0; } @@ -523,6 +524,7 @@ static int park_app_exec(struct ast_channel *chan, const char *data) res = ast_bridge_features_init(&chan_features); if (res) { ast_bridge_features_cleanup(&chan_features); + publish_parked_call_failure(chan); return -1; } diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c index f9295ccc3..8f563f7cf 100644 --- a/res/parking/parking_bridge_features.c +++ b/res/parking/parking_bridge_features.c @@ -49,6 +49,7 @@ struct parked_subscription_datastore { }; struct parked_subscription_data { + struct transfer_channel_data *transfer_data; char *parkee_uuid; int hangup_after:1; char parker_uuid[0]; @@ -89,7 +90,7 @@ static void parker_parked_call_message_response(struct ast_parked_call_payload * const char *parkee_to_act_on = data->parkee_uuid; char saynum_buf[16]; struct ast_channel_snapshot *parkee_snapshot = message->parkee; - RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel *, parker, NULL, ast_channel_cleanup); RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup); if (strcmp(parkee_to_act_on, parkee_snapshot->uniqueid)) { @@ -113,22 +114,35 @@ static void parker_parked_call_message_response(struct ast_parked_call_payload * return; } + /* This subscription callback will block for the duration of the announcement if + * parked_subscription_data is tracking a transfer_channel_data struct. */ if (message->event_type == PARKED_CALL) { /* queue the saynum on the bridge channel and hangup */ snprintf(saynum_buf, sizeof(saynum_buf), "%d %u", data->hangup_after, message->parkingspace); - ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL); - wipe_subscription_datastore(bridge_channel->chan); - } - - if (message->event_type == PARKED_CALL_FAILED) { - ast_bridge_channel_queue_playfile(bridge_channel, NULL, "pbx-parkingfailed", NULL); - wipe_subscription_datastore(bridge_channel->chan); + if (!data->transfer_data) { + ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL); + } else { + ast_bridge_channel_queue_playfile_sync(bridge_channel, say_parking_space, saynum_buf, NULL); + data->transfer_data->completed = 1; + } + wipe_subscription_datastore(parker); + } else if (message->event_type == PARKED_CALL_FAILED) { + if (!data->transfer_data) { + ast_bridge_channel_queue_playfile(bridge_channel, NULL, "pbx-parkingfailed", NULL); + } else { + ast_bridge_channel_queue_playfile_sync(bridge_channel, NULL, "pbx-parkingfailed", NULL); + data->transfer_data->completed = 1; + } + wipe_subscription_datastore(parker); } } static void parker_update_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message) { if (stasis_subscription_final_message(sub, message)) { + struct parked_subscription_data *ps_data = data; + ao2_cleanup(ps_data->transfer_data); + ps_data->transfer_data = NULL; ast_free(data); return; } @@ -139,7 +153,8 @@ static void parker_update_cb(void *data, struct stasis_subscription *sub, struct } } -int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid, int hangup_after) +static int create_parked_subscription_full(struct ast_channel *chan, const char *parkee_uuid, int hangup_after, + struct transfer_channel_data *parked_channel_data) { struct ast_datastore *datastore; struct parked_subscription_datastore *parked_datastore; @@ -167,6 +182,11 @@ int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid return -1; } + if (parked_channel_data) { + subscription_data->transfer_data = parked_channel_data; + ao2_ref(parked_channel_data, +1); + } + subscription_data->hangup_after = hangup_after; subscription_data->parkee_uuid = subscription_data->parker_uuid + parker_uuid_size; strcpy(subscription_data->parkee_uuid, parkee_uuid); @@ -185,13 +205,18 @@ int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid return 0; } +int create_parked_subscription(struct ast_channel *chan, const char *parkee_uuid, int hangup_after) +{ + return create_parked_subscription_full(chan, parkee_uuid, hangup_after, NULL); +} + /*! * \internal * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly * identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the * local channel and the channel that instigated the park. */ -static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten) +static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten, struct transfer_channel_data *parked_channel_data) { char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; struct ast_channel *parkee; @@ -220,7 +245,11 @@ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const ast_channel_unlock(parkee); /* We need to have the parker subscribe to the new local channel before hand. */ - create_parked_subscription(parker, ast_channel_uniqueid(parkee_side_2), 1); + if (create_parked_subscription_full(parker, ast_channel_uniqueid(parkee_side_2), 1, parked_channel_data)) { + ast_channel_unref(parkee_side_2); + ast_hangup(parkee); + return NULL; + } ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0); @@ -272,14 +301,21 @@ static int parking_is_exten_park(const char *context, const char *exten) * \param bridge_channel The bridge_channel representing the channel performing the park * \param context The context to blind transfer to * \param exten The extension to blind transfer to + * \param parked_channel_cb Optional callback executed prior to sending the parked channel into the bridge + * \param parked_channel_data Data for the parked_channel_cb * * \retval 0 on success * \retval non-zero on error */ static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel, - const char *context, const char *exten) + const char *context, const char *exten, transfer_channel_cb parked_channel_cb, + struct transfer_channel_data *parked_channel_data) { RAII_VAR(struct ast_bridge_channel *, other, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel *, other_chan, NULL, ast_channel_cleanup); + + struct ast_exten *e; + struct pbx_find_info find_info = { .stacklen = 0 }; int peer_count; if (ast_strlen_zero(context) || ast_strlen_zero(exten)) { @@ -299,6 +335,8 @@ static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel if (peer_count == 2) { other = ast_bridge_channel_peer(bridge_channel); ao2_ref(other, +1); + other_chan = other->chan; + ast_channel_ref(other_chan); } ast_bridge_unlock(bridge_channel->bridge); @@ -313,34 +351,48 @@ static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel if (peer_count > 2) { struct ast_channel *transfer_chan = NULL; - transfer_chan = park_local_transfer(bridge_channel->chan, context, exten); + transfer_chan = park_local_transfer(bridge_channel->chan, context, exten, parked_channel_data); if (!transfer_chan) { return -1; } + ast_channel_ref(transfer_chan); + + if (parked_channel_cb) { + parked_channel_cb(transfer_chan, parked_channel_data, AST_BRIDGE_TRANSFER_MULTI_PARTY); + } if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_hangup(transfer_chan); + ast_channel_unref(transfer_chan); return -1; } + + ast_channel_unref(transfer_chan); + return 0; } /* Subscribe to park messages with the other channel entering */ - if (create_parked_subscription(bridge_channel->chan, ast_channel_uniqueid(other->chan), 1)) { + if (create_parked_subscription_full(bridge_channel->chan, ast_channel_uniqueid(other->chan), 1, parked_channel_data)) { return -1; } + if (parked_channel_cb) { + parked_channel_cb(other_chan, parked_channel_data, AST_BRIDGE_TRANSFER_SINGLE_PARTY); + } + + e = pbx_find_extension(NULL, NULL, &find_info, context, exten, 1, NULL, NULL, E_MATCH); + /* Write the park frame with the intended recipient and other data out to the bridge. */ ast_bridge_channel_write_park(bridge_channel, - ast_channel_uniqueid(other->chan), + ast_channel_uniqueid(other_chan), ast_channel_uniqueid(bridge_channel->chan), - NULL); + e ? ast_get_extension_app_data(e) : NULL); return 0; } - /*! * \internal * \since 12.0.0 @@ -444,7 +496,7 @@ static int parking_park_call(struct ast_bridge_channel *parker, char *exten, siz if (exten) { ast_copy_string(exten, lot->cfg->parkext, length); } - return parking_blind_transfer_park(parker, lot->cfg->parking_con, lot->cfg->parkext); + return parking_blind_transfer_park(parker, lot->cfg->parking_con, lot->cfg->parkext, NULL, NULL); } static int feature_park_call(struct ast_bridge_channel *bridge_channel, void *hook_pvt) diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c index e6855ae47..20e3012b1 100644 --- a/res/res_pjsip_refer.c +++ b/res/res_pjsip_refer.c @@ -55,6 +55,8 @@ struct refer_progress { struct ast_taskprocessor *serializer; /*! \brief Stasis subscription for bridge events */ struct stasis_subscription *bridge_sub; + /*! \brief Reference to transfer_channel_data related to the refer */ + struct transfer_channel_data *transfer_data; /*! \brief Uniqueid of transferee channel */ char *transferee; }; @@ -165,6 +167,12 @@ static void refer_progress_bridge(void *data, struct stasis_subscription *sub, return; } + if (!progress->transfer_data->completed) { + /* We can't act on this message because the transfer_channel_data doesn't show that + * the transfer is ready to progress */ + return; + } + /* OMG the transferee is joining a bridge. His call got answered! */ notification = refer_progress_notification_alloc(progress, 200, PJSIP_EVSUB_STATE_TERMINATED); if (notification) { @@ -186,6 +194,11 @@ static struct ast_frame *refer_progress_framehook(struct ast_channel *chan, stru return f; } + /* If the completed flag hasn't been raised, skip this pass. */ + if (!progress->transfer_data->completed) { + return f; + } + /* Determine the state of the REFER based on the control frames (or voice frames) passing */ if (f->frametype == AST_FRAME_VOICE && !progress->subclass) { /* Media is passing without progress, this means the call has been answered */ @@ -240,6 +253,10 @@ static void refer_progress_framehook_destroy(void *data) ao2_cleanup(notification); } + if (progress->bridge_sub) { + progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub); + } + ao2_cleanup(progress); } @@ -296,6 +313,8 @@ static void refer_progress_destroy(void *obj) progress->bridge_sub = stasis_unsubscribe(progress->bridge_sub); } + ao2_cleanup(progress->transfer_data); + ast_free(progress->transferee); ast_taskprocessor_unreference(progress->serializer); } @@ -472,9 +491,10 @@ struct refer_blind { }; /*! \brief Blind transfer callback function */ -static void refer_blind_callback(struct ast_channel *chan, void *user_data, enum ast_transfer_type transfer_type) +static void refer_blind_callback(struct ast_channel *chan, struct transfer_channel_data *user_data_wrapper, + enum ast_transfer_type transfer_type) { - struct refer_blind *refer = user_data; + struct refer_blind *refer = user_data_wrapper->data; pjsip_generic_string_hdr *referred_by; static const pj_str_t str_referred_by = { "Referred-By", 11 }; @@ -503,6 +523,10 @@ static void refer_blind_callback(struct ast_channel *chan, void *user_data, enum } } + /* Progress needs a reference to the transfer_channel_data so that it can track the completed status of the transfer */ + ao2_ref(user_data_wrapper, +1); + refer->progress->transfer_data = user_data_wrapper; + /* We need to bump the reference count up on the progress structure since it is in the frame hook now */ ao2_ref(refer->progress, +1); |