res_pjsip_refer: Fix bugs involving Parking/PJSIP/transfers
authorJonathan Rose <jrose@digium.com>
Thu, 22 May 2014 15:52:30 +0000 (15:52 +0000)
committerJonathan Rose <jrose@digium.com>
Thu, 22 May 2014 15:52:30 +0000 (15:52 +0000)
PJSIP would never send the final 200 Notify for a blind transfer
when transferring to parking. This patch fixes that. In addition,
it fixes a reference leak when performing blind transfers to
non-bridging extensions.

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

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

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

12 files changed:
channels/chan_dahdi.c
channels/chan_mgcp.c
channels/chan_sip.c
channels/sig_analog.c
include/asterisk/bridge.h
include/asterisk/parking.h
main/bridge.c
main/bridge_basic.c
main/parking.c
res/parking/parking_applications.c
res/parking/parking_bridge_features.c
res/res_pjsip_refer.c

index b4c8ec3..71e5831 100644 (file)
@@ -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.
index 1955eda..2d0afe2 100644 (file)
@@ -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;
index a9a1379..6c49236 100644 (file)
@@ -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);
index 21b8321..9957074 100644 (file)
@@ -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.
index 8768fe1..d23d4b5 100644 (file)
@@ -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);
 
 /*!
index 8b2b4b4..a8832cd 100644 (file)
@@ -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.
index 6fdf0d5..fa4e3c6 100644 (file)
@@ -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;
        }
index a5827f2..cee56b1 100644 (file)
@@ -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);
index db274a3..f7f1dfb 100644 (file)
@@ -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)
index 3d2eecd..5500e33 100644 (file)
@@ -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;
        }
 
index f9295cc..8f563f7 100644 (file)
@@ -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)
index e6855ae..20e3012 100644 (file)
@@ -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);