diff options
Diffstat (limited to 'res')
-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 |
3 files changed, 98 insertions, 20 deletions
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); |