DTMF atxfer: Setup recall channels as if the transferee initiated the call.
authorRichard Mudgett <rmudgett@digium.com>
Mon, 22 Dec 2014 21:20:11 +0000 (21:20 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Mon, 22 Dec 2014 21:20:11 +0000 (21:20 +0000)
After the initial DTMF atxfer call attempt to the transfer target fails to
answer during a blonde transfer, the recall callback channels do not get
setup with information from the initial transferrer channel.  As a result,
the recall callback to the transferrer does not have callid, channel
variables, datastores, accountcode, peeraccount, COLP, and CLID setup.  A
similar situation happens with the recall callback to the transfer target
but it is less visible.  The recall callback to the transfer target does
not have callid, channel variables, datastores, accountcode, peeraccount,
and COLP setup.

* Added missing information to the recall callback channels before
initiating the call.  callid, channel variables, datastores, accountcode,
peeraccount, COLP, and CLID

* Set callid of the transferrer channel on the DTMF atxfer controller
thread attended_transfer_monitor_thread().

* Added missing channel unlocks and props unref to off nominal paths in
attended_transfer_properties_alloc().

ASTERISK-23841 #close
Reported by: Richard Mudgett

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

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

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

main/bridge_basic.c

index b5305a6..48b791d 100644 (file)
@@ -45,6 +45,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/app.h"
 #include "asterisk/dial.h"
 #include "asterisk/stasis_bridges.h"
+#include "asterisk/stasis_channels.h"
 #include "asterisk/features.h"
 #include "asterisk/format_cache.h"
 #include "asterisk/test.h"
@@ -1347,6 +1348,8 @@ struct attended_transfer_properties {
        struct ast_dial *dial;
        /*! The bridging features the transferer has available */
        struct ast_flags transferer_features;
+       /*! Saved transferer connected line data for recalling the transferer. */
+       struct ast_party_connected_line original_transferer_colp;
 };
 
 static void attended_transfer_properties_destructor(void *obj)
@@ -1361,6 +1364,7 @@ static void attended_transfer_properties_destructor(void *obj)
        ast_channel_cleanup(props->transferer);
        ast_channel_cleanup(props->transfer_target);
        ast_channel_cleanup(props->recall_target);
+       ast_party_connected_line_free(&props->original_transferer_colp);
        ast_string_field_free_memory(props);
        ast_cond_destroy(&props->cond);
 }
@@ -1428,6 +1432,7 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc(
        xfer_cfg = ast_get_chan_features_xfer_config(props->transferer);
        if (!xfer_cfg) {
                ast_log(LOG_ERROR, "Unable to get transfer configuration from channel %s\n", ast_channel_name(props->transferer));
+               ast_channel_unlock(props->transferer);
                ao2_ref(props, -1);
                return NULL;
        }
@@ -1443,11 +1448,20 @@ static struct attended_transfer_properties *attended_transfer_properties_alloc(
        ast_string_field_set(props, failsound, xfer_cfg->xferfailsound);
        ast_string_field_set(props, xfersound, xfer_cfg->xfersound);
 
+       /*
+        * Save the transferee's party information for any recall calls.
+        * This is the only piece of information needed that gets overwritten
+        * on the transferer channel by the inital call to the transfer target.
+        */
+       ast_party_connected_line_copy(&props->original_transferer_colp,
+               ast_channel_connected(props->transferer));
+
        tech = ast_strdupa(ast_channel_name(props->transferer));
        addr = strchr(tech, '/');
        if (!addr) {
                ast_log(LOG_ERROR, "Transferer channel name does not follow typical channel naming format (tech/address)\n");
-               ast_channel_unref(props->transferer);
+               ast_channel_unlock(props->transferer);
+               ao2_ref(props, -1);
                return NULL;
        }
        *addr++ = '\0';
@@ -2331,10 +2345,50 @@ static void recall_callback(struct ast_dial *dial)
        }
 }
 
+/*!
+ * \internal
+ * \brief Setup common things to transferrer and transfer_target recall channels.
+ *
+ * \param recall Channel for recalling a party.
+ * \param transferer Channel supplying recall information.
+ *
+ * \details
+ * Setup callid, variables, datastores, accountcode, and peeraccount.
+ *
+ * \pre Both channels are locked on entry.
+ *
+ * \pre COLP and CLID on the recall channel are setup by the caller but not
+ * explicitly published yet.
+ *
+ * \return Nothing
+ */
+static void common_recall_channel_setup(struct ast_channel *recall, struct ast_channel *transferer)
+{
+       struct ast_callid *callid;
+
+       callid = ast_read_threadstorage_callid();
+       if (callid) {
+               ast_channel_callid_set(recall, callid);
+               ast_callid_unref(callid);
+       }
+
+       ast_channel_inherit_variables(transferer, recall);
+       ast_channel_datastore_inherit(transferer, recall);
+
+       /*
+        * Stage a snapshot to ensure that a snapshot is always done
+        * on the recall channel so earler COLP and CLID setup will
+        * get published.
+        */
+       ast_channel_stage_snapshot(recall);
+       ast_channel_req_accountcodes(recall, transferer, AST_CHANNEL_REQUESTOR_REPLACEMENT);
+       ast_channel_stage_snapshot_done(recall);
+}
 
 static int recalling_enter(struct attended_transfer_properties *props)
 {
        RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup);
+       struct ast_channel *recall;
 
        if (!cap) {
                return -1;
@@ -2360,7 +2414,26 @@ static int recalling_enter(struct attended_transfer_properties *props)
                return -1;
        }
 
-       ast_dial_set_state_callback(props->dial, &recall_callback);
+       /*
+        * Setup callid, variables, datastores, accountcode, peeraccount,
+        * COLP, and CLID on the recalled transferrer.
+        */
+       recall = ast_dial_get_channel(props->dial, 0);
+       if (!recall) {
+               return -1;
+       }
+       ast_channel_lock_both(recall, props->transferer);
+
+       ast_party_caller_copy(ast_channel_caller(recall),
+               ast_channel_caller(props->transferer));
+       ast_party_connected_line_copy(ast_channel_connected(recall),
+               &props->original_transferer_colp);
+
+       common_recall_channel_setup(recall, props->transferer);
+       ast_channel_unlock(recall);
+       ast_channel_unlock(props->transferer);
+
+       ast_dial_set_state_callback(props->dial, recall_callback);
 
        ao2_ref(props, +1);
        ast_dial_set_user_data(props->dial, props);
@@ -2487,6 +2560,20 @@ static int retransfer_enter(struct attended_transfer_properties *props)
                return -1;
        }
 
+       /*
+        * Setup callid, variables, datastores, accountcode, peeraccount,
+        * and COLP on the recalled transfer target.
+        */
+       ast_channel_lock_both(props->recall_target, props->transferer);
+
+       ast_party_connected_line_copy(ast_channel_connected(props->recall_target),
+               &props->original_transferer_colp);
+       ast_party_id_reset(&ast_channel_connected(props->recall_target)->priv);
+
+       common_recall_channel_setup(props->recall_target, props->recall_target);
+       ast_channel_unlock(props->recall_target);
+       ast_channel_unlock(props->transferer);
+
        if (ast_call(props->recall_target, destination, 0)) {
                ast_log(LOG_ERROR, "Unable to place outbound call to recall target\n");
                ast_hangup(props->recall_target);
@@ -2881,6 +2968,18 @@ static enum attended_transfer_stimulus wait_for_stimulus(struct attended_transfe
 static void *attended_transfer_monitor_thread(void *data)
 {
        struct attended_transfer_properties *props = data;
+       struct ast_callid *callid;
+
+       /*
+        * Set thread callid to the transferer's callid because we
+        * are doing all this on that channel's behalf.
+        */
+       ast_channel_lock(props->transferer);
+       callid = ast_channel_callid(props->transferer);
+       ast_channel_unlock(props->transferer);
+       if (callid) {
+               ast_callid_threadassoc_add(callid);
+       }
 
        for (;;) {
                enum attended_transfer_stimulus stimulus;
@@ -2913,6 +3012,11 @@ static void *attended_transfer_monitor_thread(void *data)
 
        attended_transfer_properties_shutdown(props);
 
+       if (callid) {
+               ast_callid_unref(callid);
+               ast_callid_threadassoc_remove();
+       }
+
        return NULL;
 }